mirror of
https://github.com/microsoft/regorus.git
synced 2026-08-05 02:16:11 +00:00
test(azure_policy): add end-to-end policy test cases (#699)
50 end-to-end test cases derived from real Azure built-in policies. Each file contains a complete policy definition, sample resources, and expected evaluation results. Coverage spans storage, networking, compute, security, monitoring, database, identity, governance, and update management scenarios. Signed-off-by: Anand Krishnamoorthi <anakrish@microsoft.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
b989888dab
commit
afdb894d85
@@ -0,0 +1,103 @@
|
||||
# Copyright (c) Microsoft Corporation.
|
||||
# Licensed under the MIT License.
|
||||
|
||||
# E2E Test: Monitoring/ActivityLog_CaptureAllRegions
|
||||
# Features: AuditIfNotExists with inline existenceCondition using
|
||||
# implicit allOf over [*] wildcard fields + not-wrapping.
|
||||
#
|
||||
# NOTE: Without an alias catalog, fully-qualified field paths like
|
||||
# "Microsoft.Insights/logProfiles/locations[*]" resolve as raw object
|
||||
# keys. The test response structure mirrors this resolution.
|
||||
|
||||
policy_definition: |
|
||||
{
|
||||
"properties": {
|
||||
"displayName": "Azure Monitor should collect activity logs from all regions",
|
||||
"policyType": "BuiltIn",
|
||||
"mode": "All",
|
||||
"parameters": {
|
||||
"effect": {
|
||||
"type": "string",
|
||||
"defaultValue": "AuditIfNotExists",
|
||||
"allowedValues": ["AuditIfNotExists", "Disabled"]
|
||||
}
|
||||
},
|
||||
"policyRule": {
|
||||
"if": {
|
||||
"field": "type",
|
||||
"equals": "Microsoft.Resources/subscriptions"
|
||||
},
|
||||
"then": {
|
||||
"effect": "[parameters('effect')]",
|
||||
"details": {
|
||||
"type": "Microsoft.Insights/logProfiles",
|
||||
"existenceCondition": {
|
||||
"allOf": [
|
||||
{
|
||||
"not": {
|
||||
"field": "Microsoft.Insights/logProfiles/locations[*]",
|
||||
"notEquals": "eastus"
|
||||
}
|
||||
},
|
||||
{
|
||||
"not": {
|
||||
"field": "Microsoft.Insights/logProfiles/locations[*]",
|
||||
"notEquals": "westus"
|
||||
}
|
||||
},
|
||||
{
|
||||
"not": {
|
||||
"field": "Microsoft.Insights/logProfiles/locations[*]",
|
||||
"notEquals": "global"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cases:
|
||||
# Related resource not found → non-compliant
|
||||
- note: non_compliant_resource_not_found
|
||||
resource:
|
||||
type: "Microsoft.Resources/subscriptions"
|
||||
name: "sub-a"
|
||||
properties: {}
|
||||
host_await:
|
||||
- key:
|
||||
operation: "lookup_related_resources"
|
||||
type: "Microsoft.Insights/logProfiles"
|
||||
response: null
|
||||
want_effect: "AuditIfNotExists"
|
||||
|
||||
# Related resource found with all required regions → compliant
|
||||
- note: compliant_all_regions_present
|
||||
resource:
|
||||
type: "Microsoft.Resources/subscriptions"
|
||||
name: "sub-b"
|
||||
properties: {}
|
||||
host_await:
|
||||
- response:
|
||||
Microsoft:
|
||||
"Insights/logProfiles/locations":
|
||||
- eastus
|
||||
- westus
|
||||
- global
|
||||
want_undefined: true
|
||||
|
||||
# Related resource found but missing a region → non-compliant
|
||||
- note: non_compliant_missing_region
|
||||
resource:
|
||||
type: "Microsoft.Resources/subscriptions"
|
||||
name: "sub-c"
|
||||
properties: {}
|
||||
host_await:
|
||||
- response:
|
||||
Microsoft:
|
||||
"Insights/logProfiles/locations":
|
||||
- eastus
|
||||
- westus
|
||||
want_effect: "AuditIfNotExists"
|
||||
227
tests/azure_policy/cases/e2e_aks_zone_redundant.yaml
Normal file
227
tests/azure_policy/cases/e2e_aks_zone_redundant.yaml
Normal file
@@ -0,0 +1,227 @@
|
||||
# Copyright (c) Microsoft Corporation.
|
||||
# Licensed under the MIT License.
|
||||
|
||||
# E2E Test: Resilience/ContainerService_managedclusters_ZoneRedundant_Audit
|
||||
# Real Azure Policy: "Azure Kubernetes Service Managed Clusters should be
|
||||
# Zone Redundant"
|
||||
# Features: allOf, anyOf, field (type + alias), equals, field count with where,
|
||||
# nested field count ([*] inside [*]), less, greater, parameters() with
|
||||
# defaultValue
|
||||
|
||||
aliases: test_aliases.json
|
||||
|
||||
policy_definition: |
|
||||
{
|
||||
"properties": {
|
||||
"displayName": "[Preview]: Azure Kubernetes Service Managed Clusters should be Zone Redundant",
|
||||
"policyType": "BuiltIn",
|
||||
"mode": "Indexed",
|
||||
"parameters": {
|
||||
"effect": {
|
||||
"type": "String",
|
||||
"metadata": {
|
||||
"displayName": "Effect"
|
||||
},
|
||||
"allowedValues": ["Audit", "Deny", "Disabled"],
|
||||
"defaultValue": "Audit"
|
||||
}
|
||||
},
|
||||
"policyRule": {
|
||||
"if": {
|
||||
"allOf": [
|
||||
{
|
||||
"field": "type",
|
||||
"equals": "Microsoft.ContainerService/managedclusters"
|
||||
},
|
||||
{
|
||||
"anyOf": [
|
||||
{
|
||||
"count": {
|
||||
"field": "Microsoft.ContainerService/managedClusters/agentPoolProfiles[*]",
|
||||
"where": {
|
||||
"count": {
|
||||
"field": "Microsoft.ContainerService/managedClusters/agentPoolProfiles[*].availabilityZones[*]"
|
||||
},
|
||||
"less": 3
|
||||
}
|
||||
},
|
||||
"greater": 0
|
||||
},
|
||||
{
|
||||
"count": {
|
||||
"field": "Microsoft.ContainerService/managedClusters/agentPoolProfiles[*]",
|
||||
"where": {
|
||||
"field": "Microsoft.ContainerService/managedClusters/agentPoolProfiles[*].count",
|
||||
"less": 3
|
||||
}
|
||||
},
|
||||
"greater": 0
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"then": {
|
||||
"effect": "[parameters('effect')]"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cases:
|
||||
# =========================================================================
|
||||
# No effect — all pools have 3 AZs and count >= 3
|
||||
# =========================================================================
|
||||
|
||||
- note: pass_fully_zone_redundant
|
||||
resource:
|
||||
type: "Microsoft.ContainerService/managedclusters"
|
||||
name: "myAKS"
|
||||
location: "eastus"
|
||||
properties:
|
||||
agentPoolProfiles:
|
||||
- name: "system"
|
||||
count: 3
|
||||
availabilityZones: ["1", "2", "3"]
|
||||
- name: "user"
|
||||
count: 5
|
||||
availabilityZones: ["1", "2", "3"]
|
||||
want_undefined: true
|
||||
|
||||
# =========================================================================
|
||||
# Audit — one pool has fewer than 3 AZs
|
||||
# =========================================================================
|
||||
|
||||
- note: audit_pool_missing_az
|
||||
resource:
|
||||
type: "Microsoft.ContainerService/managedclusters"
|
||||
name: "myAKS"
|
||||
location: "eastus"
|
||||
properties:
|
||||
agentPoolProfiles:
|
||||
- name: "system"
|
||||
count: 3
|
||||
availabilityZones: ["1", "2", "3"]
|
||||
- name: "user"
|
||||
count: 3
|
||||
availabilityZones: ["1", "2"]
|
||||
want_effect: "Audit"
|
||||
|
||||
# =========================================================================
|
||||
# Audit — one pool has no AZs at all
|
||||
# =========================================================================
|
||||
|
||||
- note: audit_pool_no_azs
|
||||
resource:
|
||||
type: "Microsoft.ContainerService/managedclusters"
|
||||
name: "myAKS"
|
||||
location: "westus"
|
||||
properties:
|
||||
agentPoolProfiles:
|
||||
- name: "system"
|
||||
count: 3
|
||||
availabilityZones: ["1", "2", "3"]
|
||||
- name: "badpool"
|
||||
count: 3
|
||||
availabilityZones: []
|
||||
want_effect: "Audit"
|
||||
|
||||
# =========================================================================
|
||||
# Audit — pool count < 3 (even with 3 AZs)
|
||||
# =========================================================================
|
||||
|
||||
- note: audit_pool_low_count
|
||||
resource:
|
||||
type: "Microsoft.ContainerService/managedclusters"
|
||||
name: "myAKS"
|
||||
location: "eastus"
|
||||
properties:
|
||||
agentPoolProfiles:
|
||||
- name: "system"
|
||||
count: 2
|
||||
availabilityZones: ["1", "2", "3"]
|
||||
want_effect: "Audit"
|
||||
|
||||
# =========================================================================
|
||||
# Audit — both: pool has 2 AZs and count = 1
|
||||
# =========================================================================
|
||||
|
||||
- note: audit_both_violations
|
||||
resource:
|
||||
type: "Microsoft.ContainerService/managedclusters"
|
||||
name: "tinyAKS"
|
||||
location: "eastus"
|
||||
properties:
|
||||
agentPoolProfiles:
|
||||
- name: "system"
|
||||
count: 1
|
||||
availabilityZones: ["1"]
|
||||
want_effect: "Audit"
|
||||
|
||||
# =========================================================================
|
||||
# No effect — wrong resource type
|
||||
# =========================================================================
|
||||
|
||||
- note: skip_wrong_type
|
||||
resource:
|
||||
type: "Microsoft.Compute/virtualMachines"
|
||||
name: "myVM"
|
||||
location: "eastus"
|
||||
properties: {}
|
||||
want_undefined: true
|
||||
|
||||
# =========================================================================
|
||||
# Audit — single pool, exactly 3 AZs but count = 2
|
||||
# =========================================================================
|
||||
|
||||
- note: audit_three_azs_low_count
|
||||
resource:
|
||||
type: "Microsoft.ContainerService/managedclusters"
|
||||
name: "myAKS"
|
||||
location: "eastus"
|
||||
properties:
|
||||
agentPoolProfiles:
|
||||
- name: "system"
|
||||
count: 2
|
||||
availabilityZones: ["1", "2", "3"]
|
||||
want_effect: "Audit"
|
||||
|
||||
# =========================================================================
|
||||
# Deny — explicit effect parameter override
|
||||
# =========================================================================
|
||||
|
||||
- note: deny_with_explicit_effect
|
||||
resource:
|
||||
type: "Microsoft.ContainerService/managedclusters"
|
||||
name: "myAKS"
|
||||
location: "eastus"
|
||||
properties:
|
||||
agentPoolProfiles:
|
||||
- name: "system"
|
||||
count: 1
|
||||
availabilityZones: []
|
||||
parameters:
|
||||
effect: "Deny"
|
||||
want_effect: "Deny"
|
||||
|
||||
# =========================================================================
|
||||
# No effect — three pools, all fully zone-redundant
|
||||
# =========================================================================
|
||||
|
||||
- note: pass_three_pools_all_good
|
||||
resource:
|
||||
type: "Microsoft.ContainerService/managedclusters"
|
||||
name: "bigAKS"
|
||||
location: "eastus"
|
||||
properties:
|
||||
agentPoolProfiles:
|
||||
- name: "system"
|
||||
count: 3
|
||||
availabilityZones: ["1", "2", "3"]
|
||||
- name: "user1"
|
||||
count: 6
|
||||
availabilityZones: ["1", "2", "3"]
|
||||
- name: "user2"
|
||||
count: 9
|
||||
availabilityZones: ["1", "2", "3"]
|
||||
want_undefined: true
|
||||
192
tests/azure_policy/cases/e2e_approved_subnets_deny.yaml
Normal file
192
tests/azure_policy/cases/e2e_approved_subnets_deny.yaml
Normal file
@@ -0,0 +1,192 @@
|
||||
# Copyright (c) Microsoft Corporation.
|
||||
# Licensed under the MIT License.
|
||||
|
||||
# E2E Test: VirtualEnclaves/ApprovedVirtualNetworkSubnets_Deny
|
||||
# Real Azure Policy: "Network interfaces should be connected to an approved subnet
|
||||
# of the approved virtual network"
|
||||
# Source: regolator/policyDefinitions/VirtualEnclaves/ApprovedVirtualNetworkSubnets_Deny.json
|
||||
#
|
||||
# Features exercised:
|
||||
# - Value count with named iterator: count { value: params, name: "subnetName" }
|
||||
# - current('subnetName') to reference the iterator value
|
||||
# - concat() to build dynamic resource IDs
|
||||
# - Boolean parameter branching (allowAllSubnets true vs false)
|
||||
# - not { field like concat(...) } double-negation on wildcard array
|
||||
# - Two distinct allOf branches inside anyOf based on parameter value
|
||||
|
||||
aliases: test_aliases.json
|
||||
|
||||
policy_definition: |
|
||||
{
|
||||
"properties": {
|
||||
"displayName": "Network interfaces should be connected to an approved subnet of the approved virtual network",
|
||||
"policyType": "BuiltIn",
|
||||
"mode": "Indexed",
|
||||
"parameters": {
|
||||
"effect": {
|
||||
"type": "String",
|
||||
"defaultValue": "Deny",
|
||||
"allowedValues": ["Audit", "Deny", "Disabled"]
|
||||
},
|
||||
"virtualNetworkId": {
|
||||
"type": "String",
|
||||
"metadata": {
|
||||
"displayName": "Virtual network Id"
|
||||
}
|
||||
},
|
||||
"allowedSubnetList": {
|
||||
"type": "Array",
|
||||
"defaultValue": []
|
||||
},
|
||||
"allowAllSubnets": {
|
||||
"type": "Boolean",
|
||||
"defaultValue": true
|
||||
}
|
||||
},
|
||||
"policyRule": {
|
||||
"if": {
|
||||
"allOf": [
|
||||
{
|
||||
"field": "type",
|
||||
"equals": "Microsoft.Network/networkInterfaces"
|
||||
},
|
||||
{
|
||||
"anyOf": [
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"value": "[parameters('allowAllSubnets')]",
|
||||
"equals": true
|
||||
},
|
||||
{
|
||||
"not": {
|
||||
"field": "Microsoft.Network/networkInterfaces/ipconfigurations[*].subnet.id",
|
||||
"like": "[concat(parameters('virtualNetworkId'),'/*')]"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"value": "[parameters('allowAllSubnets')]",
|
||||
"equals": false
|
||||
},
|
||||
{
|
||||
"count": {
|
||||
"value": "[parameters('allowedSubnetList')]",
|
||||
"name": "subnetName",
|
||||
"where": {
|
||||
"field": "Microsoft.Network/networkInterfaces/ipconfigurations[*].subnet.id",
|
||||
"equals": "[concat(parameters('virtualNetworkId'),'/subnets/',current('subnetName'))]"
|
||||
}
|
||||
},
|
||||
"equals": 0
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"then": {
|
||||
"effect": "[parameters('effect')]"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cases:
|
||||
# =========================================================================
|
||||
# Branch 1: allowAllSubnets = true — any subnet in the VNet is OK
|
||||
# =========================================================================
|
||||
|
||||
- note: pass_allow_all_subnets_correct_vnet
|
||||
resource:
|
||||
type: "Microsoft.Network/networkInterfaces"
|
||||
name: "nic-good-vnet"
|
||||
properties:
|
||||
ipConfigurations:
|
||||
- properties:
|
||||
subnet:
|
||||
id: "/subscriptions/sub1/resourceGroups/rg1/providers/Microsoft.Network/virtualNetworks/vnet1/subnets/default"
|
||||
parameters:
|
||||
virtualNetworkId: "/subscriptions/sub1/resourceGroups/rg1/providers/Microsoft.Network/virtualNetworks/vnet1"
|
||||
allowAllSubnets: true
|
||||
want_undefined: true
|
||||
|
||||
- note: deny_allow_all_subnets_wrong_vnet
|
||||
resource:
|
||||
type: "Microsoft.Network/networkInterfaces"
|
||||
name: "nic-wrong-vnet"
|
||||
properties:
|
||||
ipConfigurations:
|
||||
- properties:
|
||||
subnet:
|
||||
id: "/subscriptions/sub1/resourceGroups/rg1/providers/Microsoft.Network/virtualNetworks/vnet2/subnets/default"
|
||||
parameters:
|
||||
virtualNetworkId: "/subscriptions/sub1/resourceGroups/rg1/providers/Microsoft.Network/virtualNetworks/vnet1"
|
||||
allowAllSubnets: true
|
||||
want_effect: "Deny"
|
||||
|
||||
# =========================================================================
|
||||
# Branch 2: allowAllSubnets = false — value count with subnet list
|
||||
# =========================================================================
|
||||
|
||||
- note: pass_specific_subnet_allowed
|
||||
resource:
|
||||
type: "Microsoft.Network/networkInterfaces"
|
||||
name: "nic-allowed-subnet"
|
||||
properties:
|
||||
ipConfigurations:
|
||||
- properties:
|
||||
subnet:
|
||||
id: "/subscriptions/sub1/resourceGroups/rg1/providers/Microsoft.Network/virtualNetworks/vnet1/subnets/frontend"
|
||||
parameters:
|
||||
virtualNetworkId: "/subscriptions/sub1/resourceGroups/rg1/providers/Microsoft.Network/virtualNetworks/vnet1"
|
||||
allowAllSubnets: false
|
||||
allowedSubnetList: ["frontend", "backend"]
|
||||
want_undefined: true
|
||||
|
||||
- note: deny_subnet_not_in_allowed_list
|
||||
resource:
|
||||
type: "Microsoft.Network/networkInterfaces"
|
||||
name: "nic-bad-subnet"
|
||||
properties:
|
||||
ipConfigurations:
|
||||
- properties:
|
||||
subnet:
|
||||
id: "/subscriptions/sub1/resourceGroups/rg1/providers/Microsoft.Network/virtualNetworks/vnet1/subnets/management"
|
||||
parameters:
|
||||
virtualNetworkId: "/subscriptions/sub1/resourceGroups/rg1/providers/Microsoft.Network/virtualNetworks/vnet1"
|
||||
allowAllSubnets: false
|
||||
allowedSubnetList: ["frontend", "backend"]
|
||||
want_effect: "Deny"
|
||||
|
||||
- note: deny_empty_subnet_list
|
||||
resource:
|
||||
type: "Microsoft.Network/networkInterfaces"
|
||||
name: "nic-no-list"
|
||||
properties:
|
||||
ipConfigurations:
|
||||
- properties:
|
||||
subnet:
|
||||
id: "/subscriptions/sub1/resourceGroups/rg1/providers/Microsoft.Network/virtualNetworks/vnet1/subnets/default"
|
||||
parameters:
|
||||
virtualNetworkId: "/subscriptions/sub1/resourceGroups/rg1/providers/Microsoft.Network/virtualNetworks/vnet1"
|
||||
allowAllSubnets: false
|
||||
allowedSubnetList: []
|
||||
want_effect: "Deny"
|
||||
|
||||
# =========================================================================
|
||||
# Wrong type
|
||||
# =========================================================================
|
||||
|
||||
- note: pass_wrong_type
|
||||
resource:
|
||||
type: "Microsoft.Compute/virtualMachines"
|
||||
name: "not-a-nic"
|
||||
properties: {}
|
||||
parameters:
|
||||
virtualNetworkId: "/subscriptions/sub1/resourceGroups/rg1/providers/Microsoft.Network/virtualNetworks/vnet1"
|
||||
want_undefined: true
|
||||
134
tests/azure_policy/cases/e2e_approved_vnet_audit.yaml
Normal file
134
tests/azure_policy/cases/e2e_approved_vnet_audit.yaml
Normal file
@@ -0,0 +1,134 @@
|
||||
# Copyright (c) Microsoft Corporation.
|
||||
# Licensed under the MIT License.
|
||||
|
||||
# E2E Test: Network/ApprovedVirtualNetwork_Audit
|
||||
# Real Azure Policy: "Virtual machines should be connected to an approved virtual network"
|
||||
# Source: regolator/policyDefinitions/Network/ApprovedVirtualNetwork_Audit.json
|
||||
#
|
||||
# Features exercised:
|
||||
# - not { field like concat(...) } — double negation on wildcard array
|
||||
# - concat() to build VNet prefix pattern
|
||||
# - Wildcard array alias: ipconfigurations[*].subnet.id
|
||||
|
||||
aliases: test_aliases.json
|
||||
|
||||
policy_definition: |
|
||||
{
|
||||
"properties": {
|
||||
"displayName": "Virtual machines should be connected to an approved virtual network",
|
||||
"policyType": "BuiltIn",
|
||||
"mode": "Indexed",
|
||||
"parameters": {
|
||||
"effect": {
|
||||
"type": "string",
|
||||
"defaultValue": "Audit",
|
||||
"allowedValues": ["Audit", "Deny", "Disabled"]
|
||||
},
|
||||
"virtualNetworkId": {
|
||||
"type": "string",
|
||||
"metadata": {
|
||||
"displayName": "Virtual network Id"
|
||||
}
|
||||
}
|
||||
},
|
||||
"policyRule": {
|
||||
"if": {
|
||||
"allOf": [
|
||||
{
|
||||
"field": "type",
|
||||
"equals": "Microsoft.Network/networkInterfaces"
|
||||
},
|
||||
{
|
||||
"not": {
|
||||
"field": "Microsoft.Network/networkInterfaces/ipconfigurations[*].subnet.id",
|
||||
"like": "[concat(parameters('virtualNetworkId'),'/*')]"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"then": {
|
||||
"effect": "[parameters('effect')]"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cases:
|
||||
# =========================================================================
|
||||
# NIC in approved VNet → pass
|
||||
# =========================================================================
|
||||
|
||||
- note: pass_nic_in_approved_vnet
|
||||
resource:
|
||||
type: "Microsoft.Network/networkInterfaces"
|
||||
name: "nic-good"
|
||||
properties:
|
||||
ipConfigurations:
|
||||
- properties:
|
||||
subnet:
|
||||
id: "/subscriptions/sub1/resourceGroups/rg1/providers/Microsoft.Network/virtualNetworks/approved-vnet/subnets/default"
|
||||
parameters:
|
||||
virtualNetworkId: "/subscriptions/sub1/resourceGroups/rg1/providers/Microsoft.Network/virtualNetworks/approved-vnet"
|
||||
want_undefined: true
|
||||
|
||||
- note: pass_multiple_ips_all_in_approved_vnet
|
||||
resource:
|
||||
type: "Microsoft.Network/networkInterfaces"
|
||||
name: "nic-multi-good"
|
||||
properties:
|
||||
ipConfigurations:
|
||||
- properties:
|
||||
subnet:
|
||||
id: "/subscriptions/sub1/resourceGroups/rg1/providers/Microsoft.Network/virtualNetworks/approved-vnet/subnets/subnet1"
|
||||
- properties:
|
||||
subnet:
|
||||
id: "/subscriptions/sub1/resourceGroups/rg1/providers/Microsoft.Network/virtualNetworks/approved-vnet/subnets/subnet2"
|
||||
parameters:
|
||||
virtualNetworkId: "/subscriptions/sub1/resourceGroups/rg1/providers/Microsoft.Network/virtualNetworks/approved-vnet"
|
||||
want_undefined: true
|
||||
|
||||
# =========================================================================
|
||||
# NIC in wrong VNet → audit
|
||||
# =========================================================================
|
||||
|
||||
- note: audit_nic_in_wrong_vnet
|
||||
resource:
|
||||
type: "Microsoft.Network/networkInterfaces"
|
||||
name: "nic-bad"
|
||||
properties:
|
||||
ipConfigurations:
|
||||
- properties:
|
||||
subnet:
|
||||
id: "/subscriptions/sub1/resourceGroups/rg1/providers/Microsoft.Network/virtualNetworks/other-vnet/subnets/default"
|
||||
parameters:
|
||||
virtualNetworkId: "/subscriptions/sub1/resourceGroups/rg1/providers/Microsoft.Network/virtualNetworks/approved-vnet"
|
||||
want_effect: "Audit"
|
||||
|
||||
- note: audit_one_ip_in_wrong_vnet
|
||||
resource:
|
||||
type: "Microsoft.Network/networkInterfaces"
|
||||
name: "nic-mixed-vnet"
|
||||
properties:
|
||||
ipConfigurations:
|
||||
- properties:
|
||||
subnet:
|
||||
id: "/subscriptions/sub1/resourceGroups/rg1/providers/Microsoft.Network/virtualNetworks/approved-vnet/subnets/subnet1"
|
||||
- properties:
|
||||
subnet:
|
||||
id: "/subscriptions/sub1/resourceGroups/rg1/providers/Microsoft.Network/virtualNetworks/other-vnet/subnets/subnet1"
|
||||
parameters:
|
||||
virtualNetworkId: "/subscriptions/sub1/resourceGroups/rg1/providers/Microsoft.Network/virtualNetworks/approved-vnet"
|
||||
want_effect: "Audit"
|
||||
|
||||
# =========================================================================
|
||||
# Wrong type
|
||||
# =========================================================================
|
||||
|
||||
- note: pass_wrong_type
|
||||
resource:
|
||||
type: "Microsoft.Compute/virtualMachines"
|
||||
name: "not-nic"
|
||||
properties: {}
|
||||
parameters:
|
||||
virtualNetworkId: "/subscriptions/sub1/resourceGroups/rg1/providers/Microsoft.Network/virtualNetworks/approved-vnet"
|
||||
want_undefined: true
|
||||
250
tests/azure_policy/cases/e2e_asc_internet_traffic_firewall.yaml
Normal file
250
tests/azure_policy/cases/e2e_asc_internet_traffic_firewall.yaml
Normal file
@@ -0,0 +1,250 @@
|
||||
# Copyright (c) Microsoft Corporation.
|
||||
# Licensed under the MIT License.
|
||||
|
||||
# E2E Test: Network/ASC_All_Internet_traffic_should_be_routed_via_Azure_Firewall
|
||||
# Real Azure Policy: "[Preview]: All Internet traffic should be routed via
|
||||
# your deployed Azure Firewall"
|
||||
# Source: regolator/policyDefinitions/Network/ASC_All_Internet_traffic_should_be_routed_via_Azure_Firewall.json
|
||||
#
|
||||
# Features exercised:
|
||||
# - Nested array count: subnets[*] containing ipConfigurations[*]
|
||||
# - Count in existenceCondition (503-policy gap)
|
||||
# - Double negation: not { anyOf [name excludes] }
|
||||
# - subscription().subscriptionId, first(), split(), field('fullName')
|
||||
# - empty() on doubly-nested array field
|
||||
# - AuditIfNotExists with existence count check
|
||||
# - like operator with wildcard pattern in existenceCondition
|
||||
|
||||
aliases: test_aliases.json
|
||||
|
||||
policy_definition: |
|
||||
{
|
||||
"properties": {
|
||||
"displayName": "[Preview]: All Internet traffic should be routed via your deployed Azure Firewall",
|
||||
"policyType": "BuiltIn",
|
||||
"mode": "All",
|
||||
"parameters": {
|
||||
"effect": {
|
||||
"type": "string",
|
||||
"defaultValue": "AuditIfNotExists",
|
||||
"allowedValues": ["AuditIfNotExists", "Disabled"]
|
||||
}
|
||||
},
|
||||
"policyRule": {
|
||||
"if": {
|
||||
"allOf": [
|
||||
{
|
||||
"field": "type",
|
||||
"equals": "Microsoft.Network/virtualNetworks"
|
||||
},
|
||||
{
|
||||
"count": {
|
||||
"field": "Microsoft.Network/virtualNetworks/subnets[*]",
|
||||
"where": {
|
||||
"allOf": [
|
||||
{
|
||||
"count": {
|
||||
"field": "Microsoft.Network/virtualNetworks/subnets[*].ipConfigurations[*]",
|
||||
"where": {
|
||||
"value": "[empty(field('Microsoft.Network/virtualNetworks/subnets[*].ipConfigurations[*].id'))]",
|
||||
"equals": false
|
||||
}
|
||||
},
|
||||
"greaterOrEquals": 2
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.Network/virtualNetworks/subnets[*].routeTable",
|
||||
"exists": false
|
||||
},
|
||||
{
|
||||
"not": {
|
||||
"anyOf": [
|
||||
{
|
||||
"field": "Microsoft.Network/virtualNetworks/subnets[*].name",
|
||||
"equals": "AzureBastionSubnet"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.Network/virtualNetworks/subnets[*].name",
|
||||
"equals": "GatewaySubnet"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"greater": 0
|
||||
}
|
||||
]
|
||||
},
|
||||
"then": {
|
||||
"effect": "[parameters('effect')]",
|
||||
"details": {
|
||||
"type": "Microsoft.Network/azureFirewalls",
|
||||
"existenceCondition": {
|
||||
"count": {
|
||||
"field": "Microsoft.Network/azureFirewalls/ipConfigurations[*]",
|
||||
"where": {
|
||||
"field": "Microsoft.Network/azureFirewalls/ipConfigurations[*].subnet.id",
|
||||
"like": "[concat('/subscriptions/', subscription().subscriptionId, '/resourceGroups/*/providers/Microsoft.Network/virtualNetworks/', first(split(field('fullName'), '/')), '/subnets/AzureFirewallSubnet')]"
|
||||
}
|
||||
},
|
||||
"equals": 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cases:
|
||||
# =========================================================================
|
||||
# AINE — VNet has a qualifying subnet (2+ ipConfigs, no routeTable, not
|
||||
# excluded name) and no firewall found
|
||||
# =========================================================================
|
||||
|
||||
- note: aine_qualifying_subnet_no_firewall
|
||||
resource:
|
||||
type: "Microsoft.Network/virtualNetworks"
|
||||
name: "vnet-no-fw"
|
||||
id: "/subscriptions/sub1/resourceGroups/rg1/providers/Microsoft.Network/virtualNetworks/vnet-no-fw"
|
||||
properties:
|
||||
subnets:
|
||||
- name: "WorkloadSubnet"
|
||||
properties:
|
||||
ipConfigurations:
|
||||
- id: "/subscriptions/sub1/resourceGroups/rg1/providers/Microsoft.Network/networkInterfaces/nic1/ipConfigurations/ipconfig1"
|
||||
- id: "/subscriptions/sub1/resourceGroups/rg1/providers/Microsoft.Network/networkInterfaces/nic2/ipConfigurations/ipconfig1"
|
||||
host_await:
|
||||
- key:
|
||||
operation: "lookup_related_resources"
|
||||
type: "Microsoft.Network/azureFirewalls"
|
||||
response: null
|
||||
want_effect: "AuditIfNotExists"
|
||||
|
||||
# =========================================================================
|
||||
# Pass — VNet has subnet but only 1 ipConfiguration (threshold is >=2)
|
||||
# =========================================================================
|
||||
|
||||
- note: pass_subnet_only_one_ip_config
|
||||
resource:
|
||||
type: "Microsoft.Network/virtualNetworks"
|
||||
name: "vnet-single-ip"
|
||||
properties:
|
||||
subnets:
|
||||
- name: "AppSubnet"
|
||||
properties:
|
||||
ipConfigurations:
|
||||
- id: "/subscriptions/sub1/resourceGroups/rg1/providers/Microsoft.Network/networkInterfaces/nic1/ipConfigurations/ipconfig1"
|
||||
want_undefined: true
|
||||
|
||||
# =========================================================================
|
||||
# Pass — all qualifying subnets have routeTable set
|
||||
# =========================================================================
|
||||
|
||||
- note: pass_all_subnets_have_route_table
|
||||
resource:
|
||||
type: "Microsoft.Network/virtualNetworks"
|
||||
name: "vnet-routed"
|
||||
properties:
|
||||
subnets:
|
||||
- name: "WorkloadSubnet"
|
||||
properties:
|
||||
routeTable:
|
||||
id: "/subscriptions/sub1/resourceGroups/rg1/providers/Microsoft.Network/routeTables/rt1"
|
||||
ipConfigurations:
|
||||
- id: "/subscriptions/sub1/resourceGroups/rg1/providers/nic1/ipconfig1"
|
||||
- id: "/subscriptions/sub1/resourceGroups/rg1/providers/nic2/ipconfig1"
|
||||
- id: "/subscriptions/sub1/resourceGroups/rg1/providers/nic3/ipconfig1"
|
||||
want_undefined: true
|
||||
|
||||
# =========================================================================
|
||||
# Pass — only excluded subnets (AzureBastionSubnet, GatewaySubnet)
|
||||
# =========================================================================
|
||||
|
||||
- note: pass_excluded_subnets_only
|
||||
resource:
|
||||
type: "Microsoft.Network/virtualNetworks"
|
||||
name: "vnet-bastion-gw"
|
||||
properties:
|
||||
subnets:
|
||||
- name: "AzureBastionSubnet"
|
||||
properties:
|
||||
ipConfigurations:
|
||||
- id: "/subscriptions/sub1/rg1/nic1/ip1"
|
||||
- id: "/subscriptions/sub1/rg1/nic2/ip1"
|
||||
- id: "/subscriptions/sub1/rg1/nic3/ip1"
|
||||
- name: "GatewaySubnet"
|
||||
properties:
|
||||
ipConfigurations:
|
||||
- id: "/subscriptions/sub1/rg1/nic4/ip1"
|
||||
- id: "/subscriptions/sub1/rg1/nic5/ip1"
|
||||
want_undefined: true
|
||||
|
||||
# =========================================================================
|
||||
# AINE — qualifying subnet exists + firewall found but no matching
|
||||
# ipConfiguration for this VNet
|
||||
# =========================================================================
|
||||
|
||||
- note: aine_firewall_no_matching_subnet
|
||||
resource:
|
||||
type: "Microsoft.Network/virtualNetworks"
|
||||
name: "vnet-no-match"
|
||||
id: "/subscriptions/sub1/resourceGroups/rg1/providers/Microsoft.Network/virtualNetworks/vnet-no-match"
|
||||
properties:
|
||||
subnets:
|
||||
- name: "AppSubnet"
|
||||
properties:
|
||||
ipConfigurations:
|
||||
- id: "/subscriptions/sub1/rg1/nic1/ip1"
|
||||
- id: "/subscriptions/sub1/rg1/nic2/ip1"
|
||||
host_await:
|
||||
- response:
|
||||
properties:
|
||||
ipConfigurations:
|
||||
- properties:
|
||||
subnet:
|
||||
id: "/subscriptions/sub1/resourceGroups/rg1/providers/Microsoft.Network/virtualNetworks/other-vnet/subnets/AzureFirewallSubnet"
|
||||
want_effect: "AuditIfNotExists"
|
||||
|
||||
# =========================================================================
|
||||
# Pass — qualifying subnet exists + firewall with matching VNet
|
||||
# =========================================================================
|
||||
|
||||
- note: pass_firewall_matches_vnet
|
||||
context:
|
||||
resourceGroup:
|
||||
name: "rg1"
|
||||
location: "eastus"
|
||||
subscription:
|
||||
subscriptionId: "sub1"
|
||||
resource:
|
||||
type: "Microsoft.Network/virtualNetworks"
|
||||
name: "vnet-protected"
|
||||
id: "/subscriptions/sub1/resourceGroups/rg1/providers/Microsoft.Network/virtualNetworks/vnet-protected"
|
||||
properties:
|
||||
subnets:
|
||||
- name: "WorkloadSubnet"
|
||||
properties:
|
||||
ipConfigurations:
|
||||
- id: "/subscriptions/sub1/rg1/nic1/ip1"
|
||||
- id: "/subscriptions/sub1/rg1/nic2/ip1"
|
||||
host_await:
|
||||
- response:
|
||||
properties:
|
||||
ipConfigurations:
|
||||
- properties:
|
||||
subnet:
|
||||
id: "/subscriptions/sub1/resourceGroups/rg1/providers/Microsoft.Network/virtualNetworks/vnet-protected/subnets/AzureFirewallSubnet"
|
||||
want_undefined: true
|
||||
|
||||
# =========================================================================
|
||||
# Wrong type → skip
|
||||
# =========================================================================
|
||||
|
||||
- note: skip_wrong_type
|
||||
resource:
|
||||
type: "Microsoft.Network/networkSecurityGroups"
|
||||
name: "nsg1"
|
||||
properties: {}
|
||||
want_undefined: true
|
||||
785
tests/azure_policy/cases/e2e_automanage_deployv2.yaml
Normal file
785
tests/azure_policy/cases/e2e_automanage_deployv2.yaml
Normal file
@@ -0,0 +1,785 @@
|
||||
# Copyright (c) Microsoft Corporation.
|
||||
# Licensed under the MIT License.
|
||||
|
||||
# E2E Test: Automanage/Deployv2
|
||||
# Real Azure Policy: "Configure virtual machines to be onboarded to Azure Automanage"
|
||||
# Source: regolator/policyDefinitions/Automanage/Deployv2.json
|
||||
#
|
||||
# Features exercised:
|
||||
# - 106 condition nodes (broad, depth 4) — wide register pressure test
|
||||
# - Dynamic tag field: [concat('tags[', parameters('inclusionTagName'), ']')]
|
||||
# - DeployIfNotExists with conditional deployment (VM vs Arc)
|
||||
# - Large hardcoded location list
|
||||
# - Extensive image publisher/offer/SKU matching
|
||||
|
||||
aliases: test_aliases.json
|
||||
|
||||
policy_definition: |
|
||||
{
|
||||
"properties": {
|
||||
"policyType": "BuiltIn",
|
||||
"mode": "Indexed",
|
||||
"displayName": "Configure virtual machines to be onboarded to Azure Automanage",
|
||||
"description": "Azure Automanage enrolls, configures, and monitors virtual machines with best practice as defined in the Microsoft Cloud Adoption Framework for Azure. Use this policy to apply Automanage to your selected scope.",
|
||||
"version": "2.4.0",
|
||||
"parameters": {
|
||||
"configurationProfileAssignment": {
|
||||
"type": "String",
|
||||
"metadata": {
|
||||
"displayName": "Configuration profile",
|
||||
"description": "The management services provided are based on whether the machine is intended to be used in a dev/test environment or production."
|
||||
},
|
||||
"allowedValues": [
|
||||
"/providers/Microsoft.Automanage/bestPractices/azurebestpracticesproduction",
|
||||
"/providers/Microsoft.Automanage/bestPractices/azurebestpracticesdevtest"
|
||||
],
|
||||
"defaultValue": "/providers/Microsoft.Automanage/bestPractices/azurebestpracticesproduction"
|
||||
},
|
||||
"effect": {
|
||||
"type": "String",
|
||||
"metadata": {
|
||||
"displayName": "Effect",
|
||||
"description": "Enable or disable the execution of this policy"
|
||||
},
|
||||
"allowedValues": [
|
||||
"AuditIfNotExists",
|
||||
"DeployIfNotExists",
|
||||
"Disabled"
|
||||
],
|
||||
"defaultValue": "DeployIfNotExists"
|
||||
},
|
||||
"inclusionTagName": {
|
||||
"type": "String",
|
||||
"metadata": {
|
||||
"displayName": "Inclusion Tag Name",
|
||||
"description": "Name of the tag to use for including VMs in the scope of this policy. This should be used along with the Inclusion Tag Value parameter."
|
||||
},
|
||||
"defaultValue": ""
|
||||
},
|
||||
"inclusionTagValues": {
|
||||
"type": "Array",
|
||||
"metadata": {
|
||||
"displayName": "Inclusion Tag Values",
|
||||
"description": "Value of the tag to use for including VMs in the scope of this policy (in case of multiple values, use a comma-separated list). This should be used along with the Inclusion Tag Name parameter."
|
||||
},
|
||||
"defaultValue": []
|
||||
}
|
||||
},
|
||||
"policyRule": {
|
||||
"if": {
|
||||
"allOf": [
|
||||
{
|
||||
"anyOf": [
|
||||
{
|
||||
"field": "[concat('tags[', parameters('inclusionTagName'), ']')]",
|
||||
"in": "[parameters('inclusionTagValues')]"
|
||||
},
|
||||
{
|
||||
"value": "[empty(parameters('inclusionTagValues'))]",
|
||||
"equals": "true"
|
||||
},
|
||||
{
|
||||
"value": "[empty(parameters('inclusionTagName'))]",
|
||||
"equals": "true"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"field": "location",
|
||||
"in": [
|
||||
"eastus",
|
||||
"eastus2",
|
||||
"westus",
|
||||
"westus2",
|
||||
"centralus",
|
||||
"southcentralus",
|
||||
"westcentralus",
|
||||
"northeurope",
|
||||
"westeurope",
|
||||
"canadacentral",
|
||||
"japaneast",
|
||||
"uksouth",
|
||||
"australiaeast",
|
||||
"australiasoutheast",
|
||||
"southeastasia"
|
||||
]
|
||||
},
|
||||
{
|
||||
"field": "type",
|
||||
"in": [
|
||||
"Microsoft.Compute/virtualMachines",
|
||||
"Microsoft.HybridCompute/machines"
|
||||
]
|
||||
},
|
||||
{
|
||||
"anyOf": [
|
||||
{
|
||||
"field": "Microsoft.Compute/imagePublisher",
|
||||
"in": [
|
||||
"esri",
|
||||
"incredibuild",
|
||||
"MicrosoftDynamicsAX",
|
||||
"MicrosoftSharepoint",
|
||||
"MicrosoftVisualStudio",
|
||||
"MicrosoftWindowsDesktop",
|
||||
"MicrosoftWindowsServerHPCPack"
|
||||
]
|
||||
},
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"field": "Microsoft.Compute/imagePublisher",
|
||||
"equals": "MicrosoftWindowsServer"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.Compute/imageSKU",
|
||||
"notLike": "2008*"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"field": "Microsoft.Compute/imagePublisher",
|
||||
"equals": "MicrosoftSQLServer"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.Compute/imageOffer",
|
||||
"notLike": "SQL2008*"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"field": "Microsoft.Compute/imagePublisher",
|
||||
"equals": "microsoft-dsvm"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.Compute/imageOffer",
|
||||
"equals": "dsvm-windows"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"field": "Microsoft.Compute/imagePublisher",
|
||||
"equals": "microsoft-ads"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.Compute/imageOffer",
|
||||
"in": [
|
||||
"standard-data-science-vm",
|
||||
"windows-data-science-vm"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"field": "Microsoft.Compute/imagePublisher",
|
||||
"equals": "batch"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.Compute/imageOffer",
|
||||
"equals": "rendering-windows2016"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"field": "Microsoft.Compute/imagePublisher",
|
||||
"equals": "center-for-internet-security-inc"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.Compute/imageOffer",
|
||||
"like": "cis-windows-server-201*"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"field": "Microsoft.Compute/imagePublisher",
|
||||
"equals": "pivotal"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.Compute/imageOffer",
|
||||
"like": "bosh-windows-server*"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"field": "Microsoft.Compute/imagePublisher",
|
||||
"equals": "cloud-infrastructure-services"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.Compute/imageOffer",
|
||||
"like": "ad*"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"anyOf": [
|
||||
{
|
||||
"field": "Microsoft.Compute/virtualMachines/osProfile.windowsConfiguration",
|
||||
"exists": "true"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.Compute/virtualMachines/storageProfile.osDisk.osType",
|
||||
"like": "Windows*"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.Compute/virtualMachines/storageProfile.imageReference.id",
|
||||
"exists": "true"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.Compute/virtualMachines/storageProfile.imageReference.sku",
|
||||
"exists": "false"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.Compute/imagePublisher",
|
||||
"in": [
|
||||
"microsoft-aks",
|
||||
"qubole-inc",
|
||||
"datastax",
|
||||
"couchbase",
|
||||
"scalegrid",
|
||||
"checkpoint",
|
||||
"paloaltonetworks",
|
||||
"debian"
|
||||
]
|
||||
},
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"field": "Microsoft.Compute/imagePublisher",
|
||||
"equals": "OpenLogic"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.Compute/imageOffer",
|
||||
"like": "CentOS*"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.Compute/imageSKU",
|
||||
"notLike": "6*"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"field": "Microsoft.Compute/imagePublisher",
|
||||
"equals": "OpenLogic"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.Compute/imageOffer",
|
||||
"like": "CentOS*"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.Compute/imageSKU",
|
||||
"notLike": "8*"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"field": "Microsoft.Compute/imagePublisher",
|
||||
"equals": "RedHat"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.Compute/imageOffer",
|
||||
"in": [
|
||||
"RHEL",
|
||||
"RHEL-HA",
|
||||
"RHEL-SAP",
|
||||
"RHEL-SAP-APPS",
|
||||
"RHEL-SAP-HA",
|
||||
"RHEL-SAP-HANA",
|
||||
"rhel-raw"
|
||||
]
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.Compute/imageSKU",
|
||||
"notLike": "6*"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"field": "Microsoft.Compute/imagePublisher",
|
||||
"equals": "RedHat"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.Compute/imageOffer",
|
||||
"in": [
|
||||
"RHEL",
|
||||
"RHEL-HA",
|
||||
"RHEL-SAP",
|
||||
"RHEL-SAP-APPS",
|
||||
"RHEL-SAP-HA",
|
||||
"RHEL-SAP-HANA",
|
||||
"rhel-raw"
|
||||
]
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.Compute/imageSKU",
|
||||
"notLike": "8*"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"field": "Microsoft.Compute/imagePublisher",
|
||||
"equals": "RedHat"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.Compute/imageOffer",
|
||||
"in": [
|
||||
"osa",
|
||||
"rhel-byos"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"field": "Microsoft.Compute/imagePublisher",
|
||||
"equals": "center-for-internet-security-inc"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.Compute/imageOffer",
|
||||
"in": [
|
||||
"cis-centos-7-l1",
|
||||
"cis-centos-7-v2-1-1-l1",
|
||||
"cis-nginx-centos-7-v1-1-0-l1",
|
||||
"cis-oracle-linux-7-v2-0-0-l1",
|
||||
"cis-postgresql-11-centos-linux-7-level-1",
|
||||
"cis-rhel-7-l2",
|
||||
"cis-rhel-7-v2-2-0-l1",
|
||||
"cis-suse-linux-12-v2-0-0-l1",
|
||||
"cis-suse15-l1",
|
||||
"cis-ubuntu-linux-1604-v1-0-0-l1",
|
||||
"cis-ubuntu-linux-1804-l1",
|
||||
"cis-ubuntu-linux-2004-l1"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"field": "Microsoft.Compute/imagePublisher",
|
||||
"equals": "credativ"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"field": "Microsoft.Compute/imagePublisher",
|
||||
"equals": "Suse"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.Compute/imageOffer",
|
||||
"like": "SLES*"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.Compute/imageSKU",
|
||||
"notLike": "11*"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"field": "Microsoft.Compute/imagePublisher",
|
||||
"equals": "Canonical"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.Compute/imageOffer",
|
||||
"equals": "UbuntuServer"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.Compute/imageSKU",
|
||||
"notLike": "12*"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"field": "Microsoft.Compute/imagePublisher",
|
||||
"equals": "microsoft-dsvm"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.Compute/imageOffer",
|
||||
"in": [
|
||||
"linux-data-science-vm-ubuntu",
|
||||
"azureml"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"field": "Microsoft.Compute/imagePublisher",
|
||||
"equals": "cloudera"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.Compute/imageOffer",
|
||||
"equals": "cloudera-centos-os"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.Compute/imageSKU",
|
||||
"notLike": "6*"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"field": "Microsoft.Compute/imagePublisher",
|
||||
"equals": "cloudera"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.Compute/imageOffer",
|
||||
"equals": "cloudera-altus-centos-os"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"field": "Microsoft.Compute/imagePublisher",
|
||||
"equals": "microsoft-ads"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.Compute/imageOffer",
|
||||
"like": "linux*"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"anyOf": [
|
||||
{
|
||||
"field": "Microsoft.Compute/virtualMachines/osProfile.linuxConfiguration",
|
||||
"exists": "true"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.Compute/virtualMachines/storageProfile.osDisk.osType",
|
||||
"like": "Linux*"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.Compute/virtualMachines/storageProfile.imageReference.id",
|
||||
"exists": "true"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.Compute/virtualMachines/storageProfile.imageReference.sku",
|
||||
"exists": "false"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"field": "Microsoft.HybridCompute/machines/osSku",
|
||||
"like": "CentOS*"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.HybridCompute/machines/osSku",
|
||||
"notLike": "Linux 6*"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"field": "Microsoft.HybridCompute/machines/osSku",
|
||||
"like": "Windows Server*"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.HybridCompute/machines/osSku",
|
||||
"notLike": "2008*"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"anyOf": [
|
||||
{
|
||||
"field": "Microsoft.HybridCompute/machines/osSku",
|
||||
"like": "Red Hat Enterprise Linux 8.*"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.HybridCompute/machines/osSku",
|
||||
"like": "Red Hat Enterprise Linux 7.*"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"anyOf": [
|
||||
{
|
||||
"field": "Microsoft.HybridCompute/machines/osSku",
|
||||
"like": "Ubuntu 18.04*"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.HybridCompute/machines/osSku",
|
||||
"like": "Ubuntu 16.04*"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.HybridCompute/machines/osSku",
|
||||
"like": "Ubuntu 20.04*"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"anyOf": [
|
||||
{
|
||||
"field": "Microsoft.HybridCompute/machines/osSku",
|
||||
"in": [
|
||||
"SUSE Linux Enterprise Server 12 SP3",
|
||||
"SUSE Linux Enterprise Server 12 SP4",
|
||||
"SUSE Linux Enterprise Server 12 SP5"
|
||||
]
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.HybridCompute/machines/osSku",
|
||||
"like": "SUSE Linux Enterprise Server 15*"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"then": {
|
||||
"effect": "[parameters('effect')]",
|
||||
"details": {
|
||||
"roleDefinitionIds": [
|
||||
"/providers/microsoft.authorization/roleDefinitions/b24988ac-6180-42a0-ab88-20f7382dd24c"
|
||||
],
|
||||
"type": "Microsoft.Automanage/configurationProfileAssignments",
|
||||
"name": "default",
|
||||
"existenceCondition": {
|
||||
"allOf": [
|
||||
{
|
||||
"field": "Microsoft.Automanage/configurationProfileAssignments/configurationProfile",
|
||||
"equals": "[parameters('configurationProfileAssignment')]"
|
||||
}
|
||||
]
|
||||
},
|
||||
"deployment": {
|
||||
"properties": {
|
||||
"mode": "incremental",
|
||||
"parameters": {
|
||||
"machineName": {
|
||||
"value": "[field('Name')]"
|
||||
},
|
||||
"resourceType": {
|
||||
"value": "[field('Type')]"
|
||||
},
|
||||
"configurationProfileAssignment": {
|
||||
"value": "[parameters('configurationProfileAssignment')]"
|
||||
}
|
||||
},
|
||||
"template": {
|
||||
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
|
||||
"contentVersion": "1.0.0.0",
|
||||
"parameters": {
|
||||
"machineName": {
|
||||
"type": "String"
|
||||
},
|
||||
"resourceType": {
|
||||
"type": "String"
|
||||
},
|
||||
"configurationProfileAssignment": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"resources": [
|
||||
{
|
||||
"condition": "[equals(toLower(parameters('resourceType')), 'microsoft.compute/virtualmachines')]",
|
||||
"type": "Microsoft.Compute/virtualMachines/providers/configurationProfileAssignments",
|
||||
"apiVersion": "2022-05-04",
|
||||
"name": "[concat(parameters('machineName'), '/Microsoft.Automanage/', 'default')]",
|
||||
"properties": {
|
||||
"configurationProfile": "[parameters('configurationProfileAssignment')]"
|
||||
}
|
||||
},
|
||||
{
|
||||
"condition": "[equals(toLower(parameters('resourceType')), 'microsoft.hybridcompute/machines')]",
|
||||
"type": "Microsoft.HybridCompute/machines/providers/configurationProfileAssignments",
|
||||
"apiVersion": "2022-05-04",
|
||||
"name": "[concat(parameters('machineName'), '/Microsoft.Automanage/', 'default')]",
|
||||
"properties": {
|
||||
"configurationProfile": "[parameters('configurationProfileAssignment')]"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cases:
|
||||
# =========================================================================
|
||||
# DINE — Windows VM in supported region, matching publisher
|
||||
# =========================================================================
|
||||
|
||||
- note: dine_windows_vm_canonical
|
||||
resource:
|
||||
type: "Microsoft.Compute/virtualMachines"
|
||||
name: "win-vm-01"
|
||||
id: "/subscriptions/sub1/resourceGroups/rg1/providers/Microsoft.Compute/virtualMachines/win-vm-01"
|
||||
location: "eastus"
|
||||
properties:
|
||||
storageProfile:
|
||||
imageReference:
|
||||
publisher: "MicrosoftWindowsServer"
|
||||
offer: "WindowsServer"
|
||||
sku: "2019-Datacenter"
|
||||
osDisk:
|
||||
osType: "Windows"
|
||||
osProfile:
|
||||
windowsConfiguration: {}
|
||||
host_await:
|
||||
- key:
|
||||
operation: "lookup_related_resources"
|
||||
type: "Microsoft.Automanage/configurationProfileAssignments"
|
||||
name: "default"
|
||||
response: null
|
||||
want_effect: "DeployIfNotExists"
|
||||
|
||||
# =========================================================================
|
||||
# Pass — VM in unsupported region
|
||||
# =========================================================================
|
||||
|
||||
- note: pass_unsupported_region
|
||||
resource:
|
||||
type: "Microsoft.Compute/virtualMachines"
|
||||
name: "vm-brazil"
|
||||
location: "brazilsoutheast"
|
||||
properties:
|
||||
storageProfile:
|
||||
imageReference:
|
||||
publisher: "MicrosoftWindowsServer"
|
||||
offer: "WindowsServer"
|
||||
sku: "2019-Datacenter"
|
||||
osDisk:
|
||||
osType: "Windows"
|
||||
osProfile:
|
||||
windowsConfiguration: {}
|
||||
want_undefined: true
|
||||
|
||||
# =========================================================================
|
||||
# DINE — Linux VM with Canonical publisher
|
||||
# =========================================================================
|
||||
|
||||
- note: dine_linux_vm_canonical
|
||||
resource:
|
||||
type: "Microsoft.Compute/virtualMachines"
|
||||
name: "linux-vm-01"
|
||||
id: "/subscriptions/sub1/resourceGroups/rg1/providers/Microsoft.Compute/virtualMachines/linux-vm-01"
|
||||
location: "westus2"
|
||||
properties:
|
||||
storageProfile:
|
||||
imageReference:
|
||||
publisher: "Canonical"
|
||||
offer: "UbuntuServer"
|
||||
sku: "18.04-LTS"
|
||||
osDisk:
|
||||
osType: "Linux"
|
||||
osProfile:
|
||||
linuxConfiguration: {}
|
||||
host_await:
|
||||
- key:
|
||||
operation: "lookup_related_resources"
|
||||
type: "Microsoft.Automanage/configurationProfileAssignments"
|
||||
name: "default"
|
||||
response: null
|
||||
want_effect: "DeployIfNotExists"
|
||||
|
||||
# =========================================================================
|
||||
# Pass — VM with tag filter that doesn't match
|
||||
# =========================================================================
|
||||
|
||||
- note: pass_tag_filter_no_match
|
||||
resource:
|
||||
type: "Microsoft.Compute/virtualMachines"
|
||||
name: "vm-tagged"
|
||||
location: "eastus"
|
||||
tags:
|
||||
env: "dev"
|
||||
properties:
|
||||
storageProfile:
|
||||
imageReference:
|
||||
publisher: "MicrosoftWindowsServer"
|
||||
offer: "WindowsServer"
|
||||
sku: "2019-Datacenter"
|
||||
osDisk:
|
||||
osType: "Windows"
|
||||
osProfile:
|
||||
windowsConfiguration: {}
|
||||
parameters:
|
||||
inclusionTagName: "env"
|
||||
inclusionTagValues:
|
||||
- "prod"
|
||||
want_undefined: true
|
||||
|
||||
# =========================================================================
|
||||
# Skip — Wrong resource type
|
||||
# =========================================================================
|
||||
|
||||
- note: skip_wrong_type
|
||||
resource:
|
||||
type: "Microsoft.Storage/storageAccounts"
|
||||
name: "storageacct1"
|
||||
location: "eastus"
|
||||
properties: {}
|
||||
want_undefined: true
|
||||
|
||||
# =========================================================================
|
||||
# Pass — VM with unsupported publisher (not in any publisher allowlist)
|
||||
# =========================================================================
|
||||
|
||||
- note: pass_unsupported_publisher
|
||||
resource:
|
||||
type: "Microsoft.Compute/virtualMachines"
|
||||
name: "vm-unknown-pub"
|
||||
id: "/subscriptions/sub1/resourceGroups/rg1/providers/Microsoft.Compute/virtualMachines/vm-unknown-pub"
|
||||
location: "eastus"
|
||||
properties:
|
||||
storageProfile:
|
||||
imageReference:
|
||||
publisher: "unknown-vendor"
|
||||
offer: "some-offer"
|
||||
sku: "some-sku"
|
||||
osDisk:
|
||||
osType: "Linux"
|
||||
osProfile:
|
||||
linuxConfiguration: {}
|
||||
want_undefined: true
|
||||
1243
tests/azure_policy/cases/e2e_azupdate_crp_autoassess_modify.yaml
Normal file
1243
tests/azure_policy/cases/e2e_azupdate_crp_autoassess_modify.yaml
Normal file
File diff suppressed because it is too large
Load Diff
1605
tests/azure_policy/cases/e2e_azupdate_customer_managed_dine.yaml
Normal file
1605
tests/azure_policy/cases/e2e_azupdate_customer_managed_dine.yaml
Normal file
File diff suppressed because it is too large
Load Diff
1609
tests/azure_policy/cases/e2e_azupdate_scheduled_patching.yaml
Normal file
1609
tests/azure_policy/cases/e2e_azupdate_scheduled_patching.yaml
Normal file
File diff suppressed because it is too large
Load Diff
572
tests/azure_policy/cases/e2e_cmk_disk_encryption.yaml
Normal file
572
tests/azure_policy/cases/e2e_cmk_disk_encryption.yaml
Normal file
@@ -0,0 +1,572 @@
|
||||
# Copyright (c) Microsoft Corporation.
|
||||
# Licensed under the MIT License.
|
||||
|
||||
# E2E Test: Compute/OSAndDataDiskCMKRequired_Deny
|
||||
# Real Azure Policy: "OS and data disks should be encrypted with a customer-managed key"
|
||||
# Features: anyOf, allOf nesting, field (type + alias), exists, equals,
|
||||
# length(), count, not, current(), multiple resource types
|
||||
# (VM, VMSS, disks, images, galleries/images/versions)
|
||||
|
||||
aliases: test_aliases.json
|
||||
|
||||
policy_definition: |
|
||||
{
|
||||
"properties": {
|
||||
"displayName": "OS and data disks should be encrypted with a customer-managed key",
|
||||
"policyType": "BuiltIn",
|
||||
"mode": "Indexed",
|
||||
"description": "Use customer-managed keys to manage the encryption at rest of the contents of your managed disks. By default, the data is encrypted at rest with platform-managed keys, but customer-managed keys are commonly required to meet regulatory compliance standards. Customer-managed keys enable the data to be encrypted with an Azure Key Vault key created and owned by you. You have full control and responsibility for the key lifecycle, including rotation and management. Learn more at https://aka.ms/disks-cmk.",
|
||||
"metadata": {
|
||||
"category": "Compute",
|
||||
"version": "3.0.0"
|
||||
},
|
||||
"version": "3.0.0",
|
||||
"parameters": {
|
||||
"effect": {
|
||||
"type": "string",
|
||||
"defaultValue": "Audit",
|
||||
"allowedValues": [
|
||||
"Audit",
|
||||
"Deny",
|
||||
"Disabled"
|
||||
],
|
||||
"metadata": {
|
||||
"displayName": "Effect",
|
||||
"description": "Enable or disable the execution of the policy"
|
||||
}
|
||||
}
|
||||
},
|
||||
"policyRule": {
|
||||
"if": {
|
||||
"anyOf": [
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"field": "type",
|
||||
"equals": "Microsoft.Compute/virtualMachines"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.Compute/virtualMachines/storageProfile.osDisk.managedDisk.diskEncryptionSet.id",
|
||||
"exists": "False"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"field": "type",
|
||||
"equals": "Microsoft.Compute/virtualMachines"
|
||||
},
|
||||
{
|
||||
"value": "[length(field('Microsoft.Compute/virtualMachines/storageProfile.dataDisks'))]",
|
||||
"greater": 0
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.Compute/virtualMachines/storageProfile.dataDisks[*].managedDisk.id",
|
||||
"exists": "False"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.Compute/virtualMachines/storageProfile.dataDisks[*].managedDisk.diskEncryptionSet.id",
|
||||
"exists": "False"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"field": "type",
|
||||
"equals": "Microsoft.Compute/virtualMachineScaleSets"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.Compute/virtualMachineScaleSets/virtualMachineProfile.storageProfile.osDisk.managedDisk.diskEncryptionSet.id",
|
||||
"exists": "False"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"field": "type",
|
||||
"equals": "Microsoft.Compute/virtualMachineScaleSets"
|
||||
},
|
||||
{
|
||||
"count": {
|
||||
"field": "Microsoft.Compute/virtualMachineScaleSets/virtualMachineProfile.storageProfile.dataDisks[*]"
|
||||
},
|
||||
"greater": 0
|
||||
},
|
||||
{
|
||||
"not": {
|
||||
"field": "Microsoft.Compute/virtualMachineScaleSets/virtualMachineProfile.storageProfile.dataDisks[*].managedDisk.diskEncryptionSet.id",
|
||||
"exists": "true"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"field": "type",
|
||||
"equals": "Microsoft.Compute/disks"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.Compute/disks/encryption.diskEncryptionSetId",
|
||||
"exists": "False"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"field": "type",
|
||||
"equals": "Microsoft.Compute/galleries/images/versions"
|
||||
},
|
||||
{
|
||||
"value": "[length(field('Microsoft.Compute/galleries/images/versions/publishingProfile.targetRegions[*].encryption.osDiskImage.diskEncryptionSetId'))]",
|
||||
"notEquals": "[length(field('Microsoft.Compute/galleries/images/versions/publishingProfile.targetRegions[*]'))]"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"field": "type",
|
||||
"equals": "Microsoft.Compute/galleries/images/versions"
|
||||
},
|
||||
{
|
||||
"value": "[length(field('Microsoft.Compute/galleries/images/versions/storageProfile.dataDiskImages[*]'))]",
|
||||
"greater": 0
|
||||
},
|
||||
{
|
||||
"anyOf": [
|
||||
{
|
||||
"count": {
|
||||
"field": "Microsoft.Compute/galleries/images/versions/publishingProfile.targetRegions[*]",
|
||||
"where": {
|
||||
"value": "[length(current('Microsoft.Compute/galleries/images/versions/publishingProfile.targetRegions[*].encryption.dataDiskImages[*].diskEncryptionSetId'))]",
|
||||
"notEquals": "[length(field('Microsoft.Compute/galleries/images/versions/storageProfile.dataDiskImages[*]'))]"
|
||||
}
|
||||
},
|
||||
"greater": 0
|
||||
},
|
||||
{
|
||||
"not": {
|
||||
"field": "Microsoft.Compute/galleries/images/versions/publishingProfile.targetRegions[*].encryption.dataDiskImages[*].diskEncryptionSetId",
|
||||
"exists": "true"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"field": "type",
|
||||
"equals": "Microsoft.Compute/images"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.Compute/images/storageProfile.osDisk.diskEncryptionSet.id",
|
||||
"exists": "False"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"field": "type",
|
||||
"equals": "Microsoft.Compute/images"
|
||||
},
|
||||
{
|
||||
"value": "[length(field('Microsoft.Compute/images/storageProfile.dataDisks[*]'))]",
|
||||
"greater": 0
|
||||
},
|
||||
{
|
||||
"not": {
|
||||
"field": "Microsoft.Compute/images/storageProfile.dataDisks[*].diskEncryptionSet.id",
|
||||
"exists": "true"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"then": {
|
||||
"effect": "[parameters('effect')]"
|
||||
}
|
||||
},
|
||||
"versions": [
|
||||
"3.0.0"
|
||||
]
|
||||
},
|
||||
"id": "/providers/Microsoft.Authorization/policyDefinitions/702dd420-7fcc-42c5-afe8-4026edd20fe0",
|
||||
"name": "702dd420-7fcc-42c5-afe8-4026edd20fe0"
|
||||
}
|
||||
|
||||
cases:
|
||||
# =========================================================================
|
||||
# 1. VM with CMK on OS disk → pass
|
||||
# =========================================================================
|
||||
|
||||
- note: pass_vm_osdisk_with_cmk
|
||||
resource:
|
||||
type: "Microsoft.Compute/virtualMachines"
|
||||
name: "vm-with-cmk"
|
||||
properties:
|
||||
storageProfile:
|
||||
osDisk:
|
||||
managedDisk:
|
||||
diskEncryptionSet:
|
||||
id: "/subscriptions/sub1/resourceGroups/rg1/providers/Microsoft.Compute/diskEncryptionSets/myDES"
|
||||
want_undefined: true
|
||||
|
||||
# =========================================================================
|
||||
# 2. VM without CMK on OS disk → Audit
|
||||
# =========================================================================
|
||||
|
||||
- note: audit_vm_osdisk_no_cmk
|
||||
resource:
|
||||
type: "Microsoft.Compute/virtualMachines"
|
||||
name: "vm-no-cmk"
|
||||
properties:
|
||||
storageProfile:
|
||||
osDisk:
|
||||
managedDisk:
|
||||
storageAccountType: "Premium_LRS"
|
||||
want_effect: "Audit"
|
||||
|
||||
# =========================================================================
|
||||
# 3. VM with CMK on OS disk and data disks having CMK → pass
|
||||
# =========================================================================
|
||||
|
||||
- note: pass_vm_osdisk_and_datadisks_with_cmk
|
||||
resource:
|
||||
type: "Microsoft.Compute/virtualMachines"
|
||||
name: "vm-all-cmk"
|
||||
properties:
|
||||
storageProfile:
|
||||
osDisk:
|
||||
managedDisk:
|
||||
diskEncryptionSet:
|
||||
id: "/subscriptions/sub1/resourceGroups/rg1/providers/Microsoft.Compute/diskEncryptionSets/myDES"
|
||||
dataDisks:
|
||||
- lun: 0
|
||||
manageddisk:
|
||||
id: "/subscriptions/sub1/resourceGroups/rg1/providers/Microsoft.Compute/disks/datadisk0"
|
||||
diskencryptionset:
|
||||
id: "/subscriptions/sub1/resourceGroups/rg1/providers/Microsoft.Compute/diskEncryptionSets/myDES"
|
||||
- lun: 1
|
||||
manageddisk:
|
||||
id: "/subscriptions/sub1/resourceGroups/rg1/providers/Microsoft.Compute/disks/datadisk1"
|
||||
diskencryptionset:
|
||||
id: "/subscriptions/sub1/resourceGroups/rg1/providers/Microsoft.Compute/diskEncryptionSets/myDES"
|
||||
want_undefined: true
|
||||
|
||||
# =========================================================================
|
||||
# 4. VM with data disks missing CMK (no managedDisk.id and no
|
||||
# diskEncryptionSet.id) → Audit
|
||||
# =========================================================================
|
||||
|
||||
- note: audit_vm_datadisks_no_cmk
|
||||
resource:
|
||||
type: "Microsoft.Compute/virtualMachines"
|
||||
name: "vm-datadisks-no-cmk"
|
||||
properties:
|
||||
storageProfile:
|
||||
osDisk:
|
||||
managedDisk:
|
||||
diskEncryptionSet:
|
||||
id: "/subscriptions/sub1/resourceGroups/rg1/providers/Microsoft.Compute/diskEncryptionSets/myDES"
|
||||
dataDisks:
|
||||
- lun: 0
|
||||
manageddisk:
|
||||
storageaccounttype: "Premium_LRS"
|
||||
- lun: 1
|
||||
manageddisk:
|
||||
storageaccounttype: "Standard_LRS"
|
||||
want_effect: "Audit"
|
||||
|
||||
# =========================================================================
|
||||
# 5. VMSS without CMK on OS disk → Audit
|
||||
# =========================================================================
|
||||
|
||||
- note: audit_vmss_osdisk_no_cmk
|
||||
resource:
|
||||
type: "Microsoft.Compute/virtualMachineScaleSets"
|
||||
name: "vmss-no-cmk"
|
||||
properties:
|
||||
virtualMachineProfile:
|
||||
storageProfile:
|
||||
osDisk:
|
||||
managedDisk:
|
||||
storageAccountType: "Premium_LRS"
|
||||
want_effect: "Audit"
|
||||
|
||||
# =========================================================================
|
||||
# 5b. VMSS with CMK on OS disk → pass
|
||||
# =========================================================================
|
||||
|
||||
- note: pass_vmss_osdisk_with_cmk
|
||||
resource:
|
||||
type: "Microsoft.Compute/virtualMachineScaleSets"
|
||||
name: "vmss-with-cmk"
|
||||
properties:
|
||||
virtualMachineProfile:
|
||||
storageProfile:
|
||||
osDisk:
|
||||
managedDisk:
|
||||
diskEncryptionSet:
|
||||
id: "/subscriptions/sub1/resourceGroups/rg1/providers/Microsoft.Compute/diskEncryptionSets/myDES"
|
||||
want_undefined: true
|
||||
|
||||
# =========================================================================
|
||||
# 5c. VMSS with data disks missing CMK → Audit
|
||||
# =========================================================================
|
||||
|
||||
- note: audit_vmss_datadisks_no_cmk
|
||||
resource:
|
||||
type: "Microsoft.Compute/virtualMachineScaleSets"
|
||||
name: "vmss-datadisks-no-cmk"
|
||||
properties:
|
||||
virtualMachineProfile:
|
||||
storageProfile:
|
||||
osDisk:
|
||||
managedDisk:
|
||||
diskEncryptionSet:
|
||||
id: "/subscriptions/sub1/resourceGroups/rg1/providers/Microsoft.Compute/diskEncryptionSets/myDES"
|
||||
dataDisks:
|
||||
- lun: 0
|
||||
managedDisk:
|
||||
storageAccountType: "Premium_LRS"
|
||||
- lun: 1
|
||||
managedDisk:
|
||||
storageAccountType: "Standard_LRS"
|
||||
want_effect: "Audit"
|
||||
|
||||
# =========================================================================
|
||||
# 5d. VMSS with data disks having CMK → pass
|
||||
# =========================================================================
|
||||
|
||||
- note: pass_vmss_datadisks_with_cmk
|
||||
resource:
|
||||
type: "Microsoft.Compute/virtualMachineScaleSets"
|
||||
name: "vmss-datadisks-cmk"
|
||||
properties:
|
||||
virtualMachineProfile:
|
||||
storageProfile:
|
||||
osDisk:
|
||||
managedDisk:
|
||||
diskEncryptionSet:
|
||||
id: "/subscriptions/sub1/resourceGroups/rg1/providers/Microsoft.Compute/diskEncryptionSets/myDES"
|
||||
dataDisks:
|
||||
- lun: 0
|
||||
managedDisk:
|
||||
diskEncryptionSet:
|
||||
id: "/subscriptions/sub1/resourceGroups/rg1/providers/Microsoft.Compute/diskEncryptionSets/myDES"
|
||||
- lun: 1
|
||||
managedDisk:
|
||||
diskEncryptionSet:
|
||||
id: "/subscriptions/sub1/resourceGroups/rg1/providers/Microsoft.Compute/diskEncryptionSets/myDES"
|
||||
want_undefined: true
|
||||
|
||||
# =========================================================================
|
||||
# 6. Disk without diskEncryptionSetId → Audit
|
||||
# =========================================================================
|
||||
|
||||
- note: audit_disk_no_des
|
||||
resource:
|
||||
type: "Microsoft.Compute/disks"
|
||||
name: "disk-no-encryption"
|
||||
properties:
|
||||
diskSizeGB: 128
|
||||
encryption:
|
||||
type: "EncryptionAtRestWithPlatformKey"
|
||||
want_effect: "Audit"
|
||||
|
||||
# =========================================================================
|
||||
# 7. Disk with diskEncryptionSetId → pass
|
||||
# =========================================================================
|
||||
|
||||
- note: pass_disk_with_des
|
||||
resource:
|
||||
type: "Microsoft.Compute/disks"
|
||||
name: "disk-with-des"
|
||||
properties:
|
||||
diskSizeGB: 128
|
||||
encryption:
|
||||
diskEncryptionSetId: "/subscriptions/sub1/resourceGroups/rg1/providers/Microsoft.Compute/diskEncryptionSets/myDES"
|
||||
type: "EncryptionAtRestWithCustomerKey"
|
||||
want_undefined: true
|
||||
|
||||
# =========================================================================
|
||||
# 8. Image without CMK on OS disk → Audit
|
||||
# =========================================================================
|
||||
|
||||
- note: audit_image_osdisk_no_cmk
|
||||
resource:
|
||||
type: "Microsoft.Compute/images"
|
||||
name: "image-no-cmk"
|
||||
properties:
|
||||
storageProfile:
|
||||
osDisk:
|
||||
osType: "Linux"
|
||||
osState: "Generalized"
|
||||
want_effect: "Audit"
|
||||
|
||||
# =========================================================================
|
||||
# 8b. Image with CMK on OS disk → pass
|
||||
# =========================================================================
|
||||
|
||||
- note: pass_image_osdisk_with_cmk
|
||||
resource:
|
||||
type: "Microsoft.Compute/images"
|
||||
name: "image-with-cmk"
|
||||
properties:
|
||||
storageProfile:
|
||||
osDisk:
|
||||
osType: "Linux"
|
||||
osState: "Generalized"
|
||||
diskEncryptionSet:
|
||||
id: "/subscriptions/sub1/resourceGroups/rg1/providers/Microsoft.Compute/diskEncryptionSets/myDES"
|
||||
want_undefined: true
|
||||
|
||||
# =========================================================================
|
||||
# 8c. Image with data disks missing CMK → Audit
|
||||
# =========================================================================
|
||||
|
||||
- note: audit_image_datadisks_no_cmk
|
||||
resource:
|
||||
type: "Microsoft.Compute/images"
|
||||
name: "image-datadisks-no-cmk"
|
||||
properties:
|
||||
storageProfile:
|
||||
osDisk:
|
||||
osType: "Linux"
|
||||
osState: "Generalized"
|
||||
diskEncryptionSet:
|
||||
id: "/subscriptions/sub1/resourceGroups/rg1/providers/Microsoft.Compute/diskEncryptionSets/myDES"
|
||||
dataDisks:
|
||||
- lun: 0
|
||||
blobUri: "https://storage.blob.core.windows.net/vhds/datadisk.vhd"
|
||||
- lun: 1
|
||||
blobUri: "https://storage.blob.core.windows.net/vhds/datadisk2.vhd"
|
||||
want_effect: "Audit"
|
||||
|
||||
# =========================================================================
|
||||
# 8d. Image with data disks having CMK → pass
|
||||
# =========================================================================
|
||||
|
||||
- note: pass_image_datadisks_with_cmk
|
||||
resource:
|
||||
type: "Microsoft.Compute/images"
|
||||
name: "image-datadisks-cmk"
|
||||
properties:
|
||||
storageProfile:
|
||||
osDisk:
|
||||
osType: "Linux"
|
||||
osState: "Generalized"
|
||||
diskEncryptionSet:
|
||||
id: "/subscriptions/sub1/resourceGroups/rg1/providers/Microsoft.Compute/diskEncryptionSets/myDES"
|
||||
dataDisks:
|
||||
- lun: 0
|
||||
diskEncryptionSet:
|
||||
id: "/subscriptions/sub1/resourceGroups/rg1/providers/Microsoft.Compute/diskEncryptionSets/myDES"
|
||||
- lun: 1
|
||||
diskEncryptionSet:
|
||||
id: "/subscriptions/sub1/resourceGroups/rg1/providers/Microsoft.Compute/diskEncryptionSets/myDES"
|
||||
want_undefined: true
|
||||
|
||||
# =========================================================================
|
||||
# Gallery Image Version: OS disk encryption (branch 6)
|
||||
# =========================================================================
|
||||
|
||||
- note: audit_gallery_version_osdisk_no_cmk
|
||||
resource:
|
||||
type: "Microsoft.Compute/galleries/images/versions"
|
||||
name: "gallery-version-osdisk-no-cmk"
|
||||
properties:
|
||||
publishingProfile:
|
||||
targetRegions:
|
||||
- name: "eastus"
|
||||
- name: "westus"
|
||||
want_effect: "Audit"
|
||||
|
||||
- note: pass_gallery_version_osdisk_with_cmk
|
||||
resource:
|
||||
type: "Microsoft.Compute/galleries/images/versions"
|
||||
name: "gallery-version-osdisk-cmk"
|
||||
properties:
|
||||
publishingProfile:
|
||||
targetRegions:
|
||||
- name: "eastus"
|
||||
encryption:
|
||||
osDiskImage:
|
||||
diskEncryptionSetId: "/subscriptions/sub1/resourceGroups/rg1/providers/Microsoft.Compute/diskEncryptionSets/myDES"
|
||||
- name: "westus"
|
||||
encryption:
|
||||
osDiskImage:
|
||||
diskEncryptionSetId: "/subscriptions/sub1/resourceGroups/rg1/providers/Microsoft.Compute/diskEncryptionSets/myDES"
|
||||
want_undefined: true
|
||||
|
||||
# =========================================================================
|
||||
# Gallery Image Version: data disk encryption (branch 7)
|
||||
# =========================================================================
|
||||
|
||||
- note: audit_gallery_version_datadisks_no_cmk
|
||||
resource:
|
||||
type: "Microsoft.Compute/galleries/images/versions"
|
||||
name: "gallery-version-datadisks-no-cmk"
|
||||
properties:
|
||||
storageProfile:
|
||||
dataDiskImages:
|
||||
- lun: 0
|
||||
- lun: 1
|
||||
publishingProfile:
|
||||
targetRegions:
|
||||
- name: "eastus"
|
||||
encryption:
|
||||
osDiskImage:
|
||||
diskEncryptionSetId: "/subscriptions/sub1/resourceGroups/rg1/providers/Microsoft.Compute/diskEncryptionSets/myDES"
|
||||
- name: "westus"
|
||||
encryption:
|
||||
osDiskImage:
|
||||
diskEncryptionSetId: "/subscriptions/sub1/resourceGroups/rg1/providers/Microsoft.Compute/diskEncryptionSets/myDES"
|
||||
want_effect: "Audit"
|
||||
|
||||
- note: pass_gallery_version_datadisks_with_cmk
|
||||
resource:
|
||||
type: "Microsoft.Compute/galleries/images/versions"
|
||||
name: "gallery-version-datadisks-cmk"
|
||||
properties:
|
||||
storageProfile:
|
||||
dataDiskImages:
|
||||
- lun: 0
|
||||
- lun: 1
|
||||
publishingProfile:
|
||||
targetRegions:
|
||||
- name: "eastus"
|
||||
encryption:
|
||||
osDiskImage:
|
||||
diskEncryptionSetId: "/subscriptions/sub1/resourceGroups/rg1/providers/Microsoft.Compute/diskEncryptionSets/myDES"
|
||||
dataDiskImages:
|
||||
- diskEncryptionSetId: "/subscriptions/sub1/resourceGroups/rg1/providers/Microsoft.Compute/diskEncryptionSets/myDES"
|
||||
- diskEncryptionSetId: "/subscriptions/sub1/resourceGroups/rg1/providers/Microsoft.Compute/diskEncryptionSets/myDES"
|
||||
- name: "westus"
|
||||
encryption:
|
||||
osDiskImage:
|
||||
diskEncryptionSetId: "/subscriptions/sub1/resourceGroups/rg1/providers/Microsoft.Compute/diskEncryptionSets/myDES"
|
||||
dataDiskImages:
|
||||
- diskEncryptionSetId: "/subscriptions/sub1/resourceGroups/rg1/providers/Microsoft.Compute/diskEncryptionSets/myDES"
|
||||
- diskEncryptionSetId: "/subscriptions/sub1/resourceGroups/rg1/providers/Microsoft.Compute/diskEncryptionSets/myDES"
|
||||
want_undefined: true
|
||||
|
||||
# =========================================================================
|
||||
# 9. Wrong resource type → pass
|
||||
# =========================================================================
|
||||
|
||||
- note: skip_wrong_type
|
||||
resource:
|
||||
type: "Microsoft.Storage/storageAccounts"
|
||||
name: "myStorage"
|
||||
properties: {}
|
||||
want_undefined: true
|
||||
162
tests/azure_policy/cases/e2e_container_diagnostics_append.yaml
Normal file
162
tests/azure_policy/cases/e2e_container_diagnostics_append.yaml
Normal file
@@ -0,0 +1,162 @@
|
||||
# Copyright (c) Microsoft Corporation.
|
||||
# Licensed under the MIT License.
|
||||
|
||||
# E2E Test: Container Instances/ContainerInstance_LogAnalytics_Append
|
||||
# Real Azure Policy: "Configure diagnostics for container group to log analytics workspace"
|
||||
# Source: regolator/policyDefinitions/Container Instances/ContainerInstance_LogAnalytics_Append.json
|
||||
#
|
||||
# Features exercised:
|
||||
# - Append effect with details array (two fields)
|
||||
# - exists "false" operator (multiple conditions)
|
||||
# - Parameterized effect (Append/Disabled)
|
||||
# - Parameters injected into details value
|
||||
|
||||
aliases: test_aliases.json
|
||||
|
||||
policy_definition: |
|
||||
{
|
||||
"properties": {
|
||||
"displayName": "Configure diagnostics for container group to log analytics workspace",
|
||||
"policyType": "BuiltIn",
|
||||
"mode": "All",
|
||||
"parameters": {
|
||||
"effect": {
|
||||
"type": "String",
|
||||
"allowedValues": ["Append", "Disabled"],
|
||||
"defaultValue": "Append"
|
||||
},
|
||||
"workspaceId": {
|
||||
"type": "String",
|
||||
"metadata": {
|
||||
"displayName": "Workspace ID"
|
||||
}
|
||||
},
|
||||
"workspaceKey": {
|
||||
"type": "String",
|
||||
"metadata": {
|
||||
"displayName": "Workspace Key"
|
||||
}
|
||||
}
|
||||
},
|
||||
"policyRule": {
|
||||
"if": {
|
||||
"allOf": [
|
||||
{
|
||||
"field": "type",
|
||||
"equals": "Microsoft.ContainerInstance/containerGroups"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.ContainerInstance/containerGroups/diagnostics.logAnalytics.workspaceId",
|
||||
"exists": "false"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.ContainerInstance/containerGroups/diagnostics.logAnalytics.workspaceKey",
|
||||
"exists": "false"
|
||||
}
|
||||
]
|
||||
},
|
||||
"then": {
|
||||
"effect": "[parameters('effect')]",
|
||||
"details": [
|
||||
{
|
||||
"field": "Microsoft.ContainerInstance/containerGroups/diagnostics.logAnalytics.workspaceId",
|
||||
"value": "[parameters('workspaceId')]"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.ContainerInstance/containerGroups/diagnostics.logAnalytics.workspaceKey",
|
||||
"value": "[parameters('workspaceKey')]"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cases:
|
||||
# =========================================================================
|
||||
# Both diagnostics fields missing → append
|
||||
# =========================================================================
|
||||
|
||||
- note: append_both_missing
|
||||
resource:
|
||||
type: "Microsoft.ContainerInstance/containerGroups"
|
||||
name: "cg-no-diag"
|
||||
properties: {}
|
||||
parameters:
|
||||
workspaceId: "workspace-guid-123"
|
||||
workspaceKey: "workspace-key-abc"
|
||||
want_effect: "Append"
|
||||
want_details:
|
||||
- field: "Microsoft.ContainerInstance/containerGroups/diagnostics.logAnalytics.workspaceId"
|
||||
value: "workspace-guid-123"
|
||||
- field: "Microsoft.ContainerInstance/containerGroups/diagnostics.logAnalytics.workspaceKey"
|
||||
value: "workspace-key-abc"
|
||||
|
||||
- note: append_diagnostics_empty
|
||||
resource:
|
||||
type: "Microsoft.ContainerInstance/containerGroups"
|
||||
name: "cg-empty-diag"
|
||||
properties:
|
||||
diagnostics: {}
|
||||
parameters:
|
||||
workspaceId: "ws-id"
|
||||
workspaceKey: "ws-key"
|
||||
want_effect: "Append"
|
||||
|
||||
# =========================================================================
|
||||
# One or both fields present → pass
|
||||
# =========================================================================
|
||||
|
||||
- note: pass_workspace_id_present
|
||||
resource:
|
||||
type: "Microsoft.ContainerInstance/containerGroups"
|
||||
name: "cg-has-id"
|
||||
properties:
|
||||
diagnostics:
|
||||
logAnalytics:
|
||||
workspaceId: "existing-id"
|
||||
parameters:
|
||||
workspaceId: "ws-id"
|
||||
workspaceKey: "ws-key"
|
||||
want_undefined: true
|
||||
|
||||
- note: pass_workspace_key_present
|
||||
resource:
|
||||
type: "Microsoft.ContainerInstance/containerGroups"
|
||||
name: "cg-has-key"
|
||||
properties:
|
||||
diagnostics:
|
||||
logAnalytics:
|
||||
workspaceKey: "existing-key"
|
||||
parameters:
|
||||
workspaceId: "ws-id"
|
||||
workspaceKey: "ws-key"
|
||||
want_undefined: true
|
||||
|
||||
- note: pass_both_present
|
||||
resource:
|
||||
type: "Microsoft.ContainerInstance/containerGroups"
|
||||
name: "cg-full-diag"
|
||||
properties:
|
||||
diagnostics:
|
||||
logAnalytics:
|
||||
workspaceId: "existing-id"
|
||||
workspaceKey: "existing-key"
|
||||
parameters:
|
||||
workspaceId: "ws-id"
|
||||
workspaceKey: "ws-key"
|
||||
want_undefined: true
|
||||
|
||||
# =========================================================================
|
||||
# Wrong type
|
||||
# =========================================================================
|
||||
|
||||
- note: pass_wrong_type
|
||||
resource:
|
||||
type: "Microsoft.Compute/virtualMachines"
|
||||
name: "not-container"
|
||||
properties: {}
|
||||
parameters:
|
||||
workspaceId: "ws-id"
|
||||
workspaceKey: "ws-key"
|
||||
want_undefined: true
|
||||
238
tests/azure_policy/cases/e2e_cosmos_firewall_audit.yaml
Normal file
238
tests/azure_policy/cases/e2e_cosmos_firewall_audit.yaml
Normal file
@@ -0,0 +1,238 @@
|
||||
# Copyright (c) Microsoft Corporation.
|
||||
# Licensed under the MIT License.
|
||||
|
||||
# E2E Test: Cosmos DB/Cosmos_NetworkRulesExist_Audit
|
||||
# Real Azure Policy: "Azure Cosmos DB accounts should have firewall rules"
|
||||
# Source: regolator/policyDefinitions/Cosmos DB/Cosmos_NetworkRulesExist_Audit.json
|
||||
#
|
||||
# Features exercised:
|
||||
# - 4-level nesting: allOf → anyOf → allOf → anyOf
|
||||
# - 3 separate count expressions (ipRules, privateEndpointConnections)
|
||||
# - exists "false" checks
|
||||
# - count field without where (plain count)
|
||||
# - count with where clause (privateLinkServiceConnectionState.status)
|
||||
# - Deeply nested sub-resource array alias
|
||||
|
||||
aliases: test_aliases.json
|
||||
|
||||
policy_definition: |
|
||||
{
|
||||
"properties": {
|
||||
"displayName": "Azure Cosmos DB accounts should have firewall rules",
|
||||
"policyType": "BuiltIn",
|
||||
"mode": "All",
|
||||
"parameters": {
|
||||
"effect": {
|
||||
"type": "String",
|
||||
"allowedValues": ["Audit", "Deny", "Disabled"],
|
||||
"defaultValue": "Deny"
|
||||
}
|
||||
},
|
||||
"policyRule": {
|
||||
"if": {
|
||||
"allOf": [
|
||||
{
|
||||
"field": "type",
|
||||
"equals": "Microsoft.DocumentDB/databaseAccounts"
|
||||
},
|
||||
{
|
||||
"anyOf": [
|
||||
{
|
||||
"field": "Microsoft.DocumentDB/databaseAccounts/publicNetworkAccess",
|
||||
"exists": "false"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.DocumentDB/databaseAccounts/publicNetworkAccess",
|
||||
"equals": "Enabled"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"anyOf": [
|
||||
{
|
||||
"field": "Microsoft.DocumentDB/databaseAccounts/isVirtualNetworkFilterEnabled",
|
||||
"exists": "false"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.DocumentDB/databaseAccounts/isVirtualNetworkFilterEnabled",
|
||||
"equals": "false"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"anyOf": [
|
||||
{
|
||||
"field": "Microsoft.DocumentDB/databaseAccounts/ipRules",
|
||||
"exists": "false"
|
||||
},
|
||||
{
|
||||
"count": {
|
||||
"field": "Microsoft.DocumentDB/databaseAccounts/ipRules[*]"
|
||||
},
|
||||
"equals": 0
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"anyOf": [
|
||||
{
|
||||
"field": "Microsoft.DocumentDB/databaseAccounts/ipRangeFilter",
|
||||
"exists": "false"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.DocumentDB/databaseAccounts/ipRangeFilter",
|
||||
"equals": ""
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"anyOf": [
|
||||
{
|
||||
"count": {
|
||||
"field": "Microsoft.DocumentDB/databaseAccounts/privateEndpointConnections[*]",
|
||||
"where": {
|
||||
"field": "Microsoft.DocumentDB/databaseAccounts/privateEndpointConnections[*].privateLinkServiceConnectionState.status",
|
||||
"equals": "Approved"
|
||||
}
|
||||
},
|
||||
"less": 1
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"then": {
|
||||
"effect": "[parameters('effect')]"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cases:
|
||||
# =========================================================================
|
||||
# Fully unprotected (public, no vnet filter, no ip rules, no PE) → deny
|
||||
# =========================================================================
|
||||
|
||||
- note: deny_completely_open
|
||||
resource:
|
||||
type: "Microsoft.DocumentDB/databaseAccounts"
|
||||
name: "cosmos-open"
|
||||
properties:
|
||||
publicNetworkAccess: "Enabled"
|
||||
isVirtualNetworkFilterEnabled: false
|
||||
ipRules: []
|
||||
ipRangeFilter: ""
|
||||
privateEndpointConnections: []
|
||||
want_effect: "Deny"
|
||||
|
||||
- note: deny_public_access_missing_fields
|
||||
resource:
|
||||
type: "Microsoft.DocumentDB/databaseAccounts"
|
||||
name: "cosmos-defaults"
|
||||
properties: {}
|
||||
want_effect: "Deny"
|
||||
|
||||
# =========================================================================
|
||||
# Protected by disabling public access → pass
|
||||
# =========================================================================
|
||||
|
||||
- note: pass_public_access_disabled
|
||||
resource:
|
||||
type: "Microsoft.DocumentDB/databaseAccounts"
|
||||
name: "cosmos-private"
|
||||
properties:
|
||||
publicNetworkAccess: "Disabled"
|
||||
want_undefined: true
|
||||
|
||||
# =========================================================================
|
||||
# Protected by vnet filter → pass
|
||||
# =========================================================================
|
||||
|
||||
- note: pass_vnet_filter_enabled
|
||||
resource:
|
||||
type: "Microsoft.DocumentDB/databaseAccounts"
|
||||
name: "cosmos-vnet"
|
||||
properties:
|
||||
publicNetworkAccess: "Enabled"
|
||||
isVirtualNetworkFilterEnabled: true
|
||||
want_undefined: true
|
||||
|
||||
# =========================================================================
|
||||
# Protected by IP rules → pass
|
||||
# =========================================================================
|
||||
|
||||
- note: pass_has_ip_rules
|
||||
resource:
|
||||
type: "Microsoft.DocumentDB/databaseAccounts"
|
||||
name: "cosmos-ip"
|
||||
properties:
|
||||
publicNetworkAccess: "Enabled"
|
||||
isVirtualNetworkFilterEnabled: false
|
||||
ipRules:
|
||||
- ipAddressOrRange: "10.0.0.1"
|
||||
want_undefined: true
|
||||
|
||||
- note: pass_has_ip_range_filter
|
||||
resource:
|
||||
type: "Microsoft.DocumentDB/databaseAccounts"
|
||||
name: "cosmos-iprange"
|
||||
properties:
|
||||
publicNetworkAccess: "Enabled"
|
||||
isVirtualNetworkFilterEnabled: false
|
||||
ipRules: []
|
||||
ipRangeFilter: "10.0.0.0/24"
|
||||
want_undefined: true
|
||||
|
||||
# =========================================================================
|
||||
# Protected by approved private endpoint → pass
|
||||
# =========================================================================
|
||||
|
||||
- note: pass_approved_private_endpoint
|
||||
resource:
|
||||
type: "Microsoft.DocumentDB/databaseAccounts"
|
||||
name: "cosmos-pe"
|
||||
properties:
|
||||
publicNetworkAccess: "Enabled"
|
||||
isVirtualNetworkFilterEnabled: false
|
||||
ipRules: []
|
||||
ipRangeFilter: ""
|
||||
privateEndpointConnections:
|
||||
- properties:
|
||||
privateLinkServiceConnectionState:
|
||||
status: "Approved"
|
||||
want_undefined: true
|
||||
|
||||
# =========================================================================
|
||||
# Private endpoint exists but not approved → deny
|
||||
# =========================================================================
|
||||
|
||||
- note: deny_pending_private_endpoint
|
||||
resource:
|
||||
type: "Microsoft.DocumentDB/databaseAccounts"
|
||||
name: "cosmos-pe-pending"
|
||||
properties:
|
||||
publicNetworkAccess: "Enabled"
|
||||
isVirtualNetworkFilterEnabled: false
|
||||
ipRules: []
|
||||
ipRangeFilter: ""
|
||||
privateEndpointConnections:
|
||||
- properties:
|
||||
privateLinkServiceConnectionState:
|
||||
status: "Pending"
|
||||
want_effect: "Deny"
|
||||
|
||||
# =========================================================================
|
||||
# Wrong type
|
||||
# =========================================================================
|
||||
|
||||
- note: pass_wrong_type
|
||||
resource:
|
||||
type: "Microsoft.Storage/storageAccounts"
|
||||
name: "not-cosmos"
|
||||
properties:
|
||||
publicNetworkAccess: "Enabled"
|
||||
want_undefined: true
|
||||
170
tests/azure_policy/cases/e2e_cosmos_locations_deny.yaml
Normal file
170
tests/azure_policy/cases/e2e_cosmos_locations_deny.yaml
Normal file
@@ -0,0 +1,170 @@
|
||||
# Copyright (c) Microsoft Corporation.
|
||||
# Licensed under the MIT License.
|
||||
|
||||
# E2E Test: Cosmos DB/Cosmos_Locations_Deny
|
||||
# Real Azure Policy: "Azure Cosmos DB allowed locations"
|
||||
# Source: regolator/policyDefinitions/Cosmos DB/Cosmos_Locations_Deny.json
|
||||
#
|
||||
# Features exercised:
|
||||
# - count with where clause
|
||||
# - Chained template functions in where: replace(toLower(first(field(...))), ' ', '')
|
||||
# - count result compared to length(field(...)) via notEquals
|
||||
# - Parameterized effect with case-variant allowedValues
|
||||
|
||||
aliases: test_aliases.json
|
||||
|
||||
policy_definition: |
|
||||
{
|
||||
"properties": {
|
||||
"displayName": "Azure Cosmos DB allowed locations",
|
||||
"policyType": "BuiltIn",
|
||||
"mode": "Indexed",
|
||||
"parameters": {
|
||||
"listOfAllowedLocations": {
|
||||
"type": "Array",
|
||||
"metadata": {
|
||||
"displayName": "Allowed locations",
|
||||
"strongType": "location"
|
||||
}
|
||||
},
|
||||
"policyEffect": {
|
||||
"type": "String",
|
||||
"allowedValues": ["audit", "Audit", "deny", "Deny", "disabled", "Disabled"],
|
||||
"defaultValue": "Deny"
|
||||
}
|
||||
},
|
||||
"policyRule": {
|
||||
"if": {
|
||||
"allOf": [
|
||||
{
|
||||
"field": "type",
|
||||
"equals": "Microsoft.DocumentDB/databaseAccounts"
|
||||
},
|
||||
{
|
||||
"count": {
|
||||
"field": "Microsoft.DocumentDB/databaseAccounts/Locations[*]",
|
||||
"where": {
|
||||
"value": "[replace(toLower(first(field('Microsoft.DocumentDB/databaseAccounts/Locations[*].locationName'))), ' ', '')]",
|
||||
"in": "[parameters('listOfAllowedLocations')]"
|
||||
}
|
||||
},
|
||||
"notEquals": "[length(field('Microsoft.DocumentDB/databaseAccounts/Locations[*]'))]"
|
||||
}
|
||||
]
|
||||
},
|
||||
"then": {
|
||||
"effect": "[parameters('policyEffect')]"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cases:
|
||||
# =========================================================================
|
||||
# All locations allowed → pass
|
||||
# =========================================================================
|
||||
|
||||
- note: pass_all_locations_in_allowed_list
|
||||
resource:
|
||||
type: "Microsoft.DocumentDB/databaseAccounts"
|
||||
name: "cosmos-compliant"
|
||||
properties:
|
||||
Locations:
|
||||
- locationName: "East US"
|
||||
- locationName: "West US"
|
||||
parameters:
|
||||
listOfAllowedLocations: ["eastus", "westus"]
|
||||
want_undefined: true
|
||||
|
||||
- note: pass_single_location_allowed
|
||||
resource:
|
||||
type: "Microsoft.DocumentDB/databaseAccounts"
|
||||
name: "cosmos-single"
|
||||
properties:
|
||||
Locations:
|
||||
- locationName: "East US"
|
||||
parameters:
|
||||
listOfAllowedLocations: ["eastus", "westus", "centralus"]
|
||||
want_undefined: true
|
||||
|
||||
# =========================================================================
|
||||
# Location not in allowed list → deny
|
||||
# =========================================================================
|
||||
|
||||
- note: deny_location_not_allowed
|
||||
resource:
|
||||
type: "Microsoft.DocumentDB/databaseAccounts"
|
||||
name: "cosmos-bad-region"
|
||||
properties:
|
||||
Locations:
|
||||
- locationName: "East US"
|
||||
- locationName: "North Europe"
|
||||
parameters:
|
||||
listOfAllowedLocations: ["eastus", "westus"]
|
||||
want_effect: "Deny"
|
||||
|
||||
- note: deny_all_locations_disallowed
|
||||
resource:
|
||||
type: "Microsoft.DocumentDB/databaseAccounts"
|
||||
name: "cosmos-all-bad"
|
||||
properties:
|
||||
Locations:
|
||||
- locationName: "South East Asia"
|
||||
- locationName: "Japan East"
|
||||
parameters:
|
||||
listOfAllowedLocations: ["eastus", "westus"]
|
||||
want_effect: "Deny"
|
||||
|
||||
- note: deny_one_of_three_disallowed
|
||||
resource:
|
||||
type: "Microsoft.DocumentDB/databaseAccounts"
|
||||
name: "cosmos-one-bad"
|
||||
properties:
|
||||
Locations:
|
||||
- locationName: "East US"
|
||||
- locationName: "West US"
|
||||
- locationName: "Brazil South"
|
||||
parameters:
|
||||
listOfAllowedLocations: ["eastus", "westus"]
|
||||
want_effect: "Deny"
|
||||
|
||||
# =========================================================================
|
||||
# Edge: location names with spaces normalized by replace+toLower
|
||||
# =========================================================================
|
||||
|
||||
- note: pass_location_with_spaces_normalized
|
||||
resource:
|
||||
type: "Microsoft.DocumentDB/databaseAccounts"
|
||||
name: "cosmos-spaces"
|
||||
properties:
|
||||
Locations:
|
||||
- locationName: "Central US"
|
||||
parameters:
|
||||
listOfAllowedLocations: ["centralus"]
|
||||
want_undefined: true
|
||||
|
||||
- note: pass_mixed_case_location
|
||||
resource:
|
||||
type: "Microsoft.DocumentDB/databaseAccounts"
|
||||
name: "cosmos-case"
|
||||
properties:
|
||||
Locations:
|
||||
- locationName: "EAST US"
|
||||
parameters:
|
||||
listOfAllowedLocations: ["eastus"]
|
||||
want_undefined: true
|
||||
|
||||
# =========================================================================
|
||||
# Wrong type
|
||||
# =========================================================================
|
||||
|
||||
- note: pass_wrong_type
|
||||
resource:
|
||||
type: "Microsoft.Storage/storageAccounts"
|
||||
name: "not-cosmos"
|
||||
properties:
|
||||
Locations:
|
||||
- locationName: "East US"
|
||||
parameters:
|
||||
listOfAllowedLocations: ["westus"]
|
||||
want_undefined: true
|
||||
445
tests/azure_policy/cases/e2e_cosmos_max_throughput.yaml
Normal file
445
tests/azure_policy/cases/e2e_cosmos_max_throughput.yaml
Normal file
@@ -0,0 +1,445 @@
|
||||
# Copyright (c) Microsoft Corporation.
|
||||
# Licensed under the MIT License.
|
||||
|
||||
# E2E Test: Cosmos DB/Cosmos_MaxThroughput_Deny
|
||||
# Real Azure Policy: "Azure Cosmos DB throughput should be limited"
|
||||
# Source: regolator/policyDefinitions/Cosmos DB/Cosmos_MaxThroughput_Deny.json
|
||||
#
|
||||
# Features exercised:
|
||||
# - allOf with nested anyOf (type check + condition check)
|
||||
# - type "like" pattern matching (*/throughputSettings)
|
||||
# - type "in" with 9 resource types
|
||||
# - Template expressions: if(), equals(), int(), field()
|
||||
# - containsKey operator
|
||||
# - exists operator
|
||||
# - greater operator with parameterized threshold
|
||||
# - Parameters: throughputMax (Integer), effect (String)
|
||||
|
||||
aliases: test_aliases.json
|
||||
|
||||
policy_definition: |
|
||||
{
|
||||
"properties": {
|
||||
"displayName": "Azure Cosmos DB throughput should be limited",
|
||||
"policyType": "BuiltIn",
|
||||
"mode": "All",
|
||||
"description": "This policy enables you to restrict the maximum throughput your organization can specify when creating Azure Cosmos DB databases and containers through the resource provider. It blocks the creation of autoscale resources.",
|
||||
"metadata": {
|
||||
"version": "1.1.0",
|
||||
"category": "Cosmos DB"
|
||||
},
|
||||
"version": "1.1.0",
|
||||
"parameters": {
|
||||
"throughputMax": {
|
||||
"type": "Integer",
|
||||
"metadata": {
|
||||
"displayName": "Max RUs",
|
||||
"description": "The maximum throughput (RU/s) that can be assigned to a container via the Resource Provider during create or update."
|
||||
}
|
||||
},
|
||||
"effect": {
|
||||
"type": "String",
|
||||
"metadata": {
|
||||
"displayName": "Policy Effect",
|
||||
"description": "The desired effect of the policy."
|
||||
},
|
||||
"allowedValues": [
|
||||
"audit",
|
||||
"Audit",
|
||||
"deny",
|
||||
"Deny",
|
||||
"disabled",
|
||||
"Disabled"
|
||||
],
|
||||
"defaultValue": "Deny"
|
||||
}
|
||||
},
|
||||
"policyRule": {
|
||||
"if": {
|
||||
"allOf": [
|
||||
{
|
||||
"anyOf": [
|
||||
{
|
||||
"field": "type",
|
||||
"like": "Microsoft.DocumentDB/databaseAccounts/*/throughputSettings"
|
||||
},
|
||||
{
|
||||
"field": "type",
|
||||
"in": [
|
||||
"Microsoft.DocumentDB/databaseAccounts/sqlDatabases",
|
||||
"Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers",
|
||||
"Microsoft.DocumentDB/databaseAccounts/mongodbDatabases",
|
||||
"Microsoft.DocumentDB/databaseAccounts/mongodbDatabases/collections",
|
||||
"Microsoft.DocumentDB/databaseAccounts/gremlinDatabases",
|
||||
"Microsoft.DocumentDB/databaseAccounts/gremlinDatabases/graphs",
|
||||
"Microsoft.DocumentDB/databaseAccounts/cassandraKeyspaces",
|
||||
"Microsoft.DocumentDB/databaseAccounts/cassandraKeyspaces/tables",
|
||||
"Microsoft.DocumentDB/databaseAccounts/tables"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"anyOf": [
|
||||
{
|
||||
"value": "[requestContext().apiVersion]",
|
||||
"less": "2019-08-01"
|
||||
},
|
||||
{
|
||||
"value": "[if(equals(field('Microsoft.DocumentDB/databaseAccounts/sqlDatabases/options.throughput'), ''), 0, int(field('Microsoft.DocumentDB/databaseAccounts/sqlDatabases/options.throughput')))]",
|
||||
"greater": "[parameters('throughputMax')]"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.DocumentDB/databaseAccounts/sqlDatabases/options",
|
||||
"containsKey": "ProvisionedThroughputSettings"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.DocumentDB/databaseAccounts/sqlDatabases/throughputSettings/default.resource.throughput",
|
||||
"greater": "[parameters('throughputMax')]"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.DocumentDB/databaseAccounts/sqlDatabases/throughputSettings/default.resource.provisionedThroughputSettings",
|
||||
"exists": "true"
|
||||
},
|
||||
{
|
||||
"value": "[if(equals(field('Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers/options.throughput'), ''), 0, int(field('Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers/options.throughput')))]",
|
||||
"greater": "[parameters('throughputMax')]"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers/options",
|
||||
"containsKey": "ProvisionedThroughputSettings"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers/throughputSettings/default.resource.throughput",
|
||||
"greater": "[parameters('throughputMax')]"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers/throughputSettings/default.resource.provisionedThroughputSettings",
|
||||
"exists": "true"
|
||||
},
|
||||
{
|
||||
"value": "[if(equals(field('Microsoft.DocumentDB/databaseAccounts/cassandraKeyspaces/options.throughput'), ''), 0, int(field('Microsoft.DocumentDB/databaseAccounts/cassandraKeyspaces/options.throughput')))]",
|
||||
"greater": "[parameters('throughputMax')]"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.DocumentDB/databaseAccounts/cassandraKeyspaces/options",
|
||||
"containsKey": "ProvisionedThroughputSettings"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.DocumentDB/databaseAccounts/cassandraKeyspaces/throughputSettings/default.resource.throughput",
|
||||
"greater": "[parameters('throughputMax')]"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.DocumentDB/databaseAccounts/cassandraKeyspaces/throughputSettings/default.resource.provisionedThroughputSettings",
|
||||
"exists": "true"
|
||||
},
|
||||
{
|
||||
"value": "[if(equals(field('Microsoft.DocumentDB/databaseAccounts/cassandraKeyspaces/tables/options.throughput'), ''), 0, int(field('Microsoft.DocumentDB/databaseAccounts/cassandraKeyspaces/tables/options.throughput')))]",
|
||||
"greater": "[parameters('throughputMax')]"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.DocumentDB/databaseAccounts/cassandraKeyspaces/tables/options",
|
||||
"containsKey": "ProvisionedThroughputSettings"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.DocumentDB/databaseAccounts/cassandraKeyspaces/tables/throughputSettings/default.resource.throughput",
|
||||
"greater": "[parameters('throughputMax')]"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.DocumentDB/databaseAccounts/cassandraKeyspaces/tables/throughputSettings/default.resource.provisionedThroughputSettings",
|
||||
"exists": "true"
|
||||
},
|
||||
{
|
||||
"value": "[if(equals(field('Microsoft.DocumentDB/databaseAccounts/gremlinDatabases/options.throughput'), ''), 0, int(field('Microsoft.DocumentDB/databaseAccounts/gremlinDatabases/options.throughput')))]",
|
||||
"greater": "[parameters('throughputMax')]"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.DocumentDB/databaseAccounts/gremlinDatabases/options",
|
||||
"containsKey": "ProvisionedThroughputSettings"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.DocumentDB/databaseAccounts/gremlinDatabases/throughputSettings/default.resource.throughput",
|
||||
"greater": "[parameters('throughputMax')]"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.DocumentDB/databaseAccounts/gremlinDatabases/throughputSettings/default.resource.provisionedThroughputSettings",
|
||||
"exists": "true"
|
||||
},
|
||||
{
|
||||
"value": "[if(equals(field('Microsoft.DocumentDB/databaseAccounts/gremlinDatabases/graphs/options.throughput'), ''), 0, int(field('Microsoft.DocumentDB/databaseAccounts/gremlinDatabases/graphs/options.throughput')))]",
|
||||
"greater": "[parameters('throughputMax')]"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.DocumentDB/databaseAccounts/gremlinDatabases/graphs/options",
|
||||
"containsKey": "ProvisionedThroughputSettings"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.DocumentDB/databaseAccounts/gremlinDatabases/graphs/throughputSettings/default.resource.throughput",
|
||||
"greater": "[parameters('throughputMax')]"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.DocumentDB/databaseAccounts/gremlinDatabases/graphs/throughputSettings/default.resource.provisionedThroughputSettings",
|
||||
"exists": "true"
|
||||
},
|
||||
{
|
||||
"value": "[if(equals(field('Microsoft.DocumentDB/databaseAccounts/mongodbDatabases/options.throughput'), ''), 0, int(field('Microsoft.DocumentDB/databaseAccounts/mongodbDatabases/options.throughput')))]",
|
||||
"greater": "[parameters('throughputMax')]"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.DocumentDB/databaseAccounts/mongodbDatabases/options",
|
||||
"containsKey": "ProvisionedThroughputSettings"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.DocumentDB/databaseAccounts/mongodbDatabases/throughputSettings/default.resource.throughput",
|
||||
"greater": "[parameters('throughputMax')]"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.DocumentDB/databaseAccounts/mongodbDatabases/throughputSettings/default.resource.provisionedThroughputSettings",
|
||||
"exists": "true"
|
||||
},
|
||||
{
|
||||
"value": "[if(equals(field('Microsoft.DocumentDB/databaseAccounts/mongodbDatabases/collections/options.throughput'), ''), 0, int(field('Microsoft.DocumentDB/databaseAccounts/mongodbDatabases/collections/options.throughput')))]",
|
||||
"greater": "[parameters('throughputMax')]"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.DocumentDB/databaseAccounts/mongodbDatabases/collections/options",
|
||||
"containsKey": "ProvisionedThroughputSettings"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.DocumentDB/databaseAccounts/mongodbDatabases/collections/throughputSettings/default.resource.throughput",
|
||||
"greater": "[parameters('throughputMax')]"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.DocumentDB/databaseAccounts/mongodbDatabases/collections/throughputSettings/default.resource.provisionedThroughputSettings",
|
||||
"exists": "true"
|
||||
},
|
||||
{
|
||||
"value": "[if(equals(field('Microsoft.DocumentDB/databaseAccounts/tables/options.throughput'), ''), 0, int(field('Microsoft.DocumentDB/databaseAccounts/tables/options.throughput')))]",
|
||||
"greater": "[parameters('throughputMax')]"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.DocumentDB/databaseAccounts/tables/options",
|
||||
"containsKey": "ProvisionedThroughputSettings"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.DocumentDB/databaseAccounts/tables/throughputSettings/default.resource.throughput",
|
||||
"greater": "[parameters('throughputMax')]"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.DocumentDB/databaseAccounts/tables/throughputSettings/default.resource.provisionedThroughputSettings",
|
||||
"exists": "true"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"then": {
|
||||
"effect": "[parameters('effect')]"
|
||||
}
|
||||
},
|
||||
"versions": [
|
||||
"1.1.0"
|
||||
]
|
||||
},
|
||||
"id": "/providers/Microsoft.Authorization/policyDefinitions/0b7ef78e-a035-4f23-b9bd-aff122a1b1cf",
|
||||
"name": "0b7ef78e-a035-4f23-b9bd-aff122a1b1cf"
|
||||
}
|
||||
|
||||
cases:
|
||||
# =========================================================================
|
||||
# SQL Database: options.throughput exceeds max → Deny
|
||||
# =========================================================================
|
||||
|
||||
- note: deny_sql_db_throughput_exceeds_max
|
||||
parameters:
|
||||
throughputMax: 400
|
||||
resource:
|
||||
type: "Microsoft.DocumentDB/databaseAccounts/sqlDatabases"
|
||||
name: "test-db"
|
||||
properties:
|
||||
options:
|
||||
throughput: "600"
|
||||
want_effect: "Deny"
|
||||
|
||||
# =========================================================================
|
||||
# SQL Database: options.throughput within max → pass
|
||||
# =========================================================================
|
||||
|
||||
- note: pass_sql_db_throughput_within_max
|
||||
parameters:
|
||||
throughputMax: 400
|
||||
resource:
|
||||
type: "Microsoft.DocumentDB/databaseAccounts/sqlDatabases"
|
||||
name: "test-db-ok"
|
||||
properties:
|
||||
options:
|
||||
throughput: "200"
|
||||
want_undefined: true
|
||||
|
||||
# =========================================================================
|
||||
# SQL Database: autoscale (ProvisionedThroughputSettings key) → Deny
|
||||
# =========================================================================
|
||||
|
||||
- note: deny_sql_db_autoscale
|
||||
parameters:
|
||||
throughputMax: 400
|
||||
resource:
|
||||
type: "Microsoft.DocumentDB/databaseAccounts/sqlDatabases"
|
||||
name: "test-db-autoscale"
|
||||
properties:
|
||||
options:
|
||||
ProvisionedThroughputSettings:
|
||||
maxThroughput: 4000
|
||||
want_effect: "Deny"
|
||||
|
||||
# =========================================================================
|
||||
# Container: options.throughput exceeds max → Deny
|
||||
# =========================================================================
|
||||
|
||||
- note: deny_container_throughput_exceeds_max
|
||||
parameters:
|
||||
throughputMax: 400
|
||||
resource:
|
||||
type: "Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers"
|
||||
name: "test-container"
|
||||
properties:
|
||||
options:
|
||||
throughput: "600"
|
||||
want_effect: "Deny"
|
||||
|
||||
# =========================================================================
|
||||
# Wrong type (parent databaseAccounts, not a sub-resource) → pass
|
||||
# =========================================================================
|
||||
|
||||
- note: pass_wrong_type
|
||||
parameters:
|
||||
throughputMax: 400
|
||||
resource:
|
||||
type: "Microsoft.DocumentDB/databaseAccounts"
|
||||
name: "test-account"
|
||||
properties: {}
|
||||
want_undefined: true
|
||||
|
||||
# =========================================================================
|
||||
# MongoDB: throughputSettings throughput exceeds max → Deny
|
||||
# =========================================================================
|
||||
|
||||
- note: deny_mongodb_throughput_settings
|
||||
parameters:
|
||||
throughputMax: 400
|
||||
resource:
|
||||
type: "Microsoft.DocumentDB/databaseAccounts/mongodbDatabases"
|
||||
name: "test-mongo"
|
||||
properties:
|
||||
"default":
|
||||
resource:
|
||||
throughput: 600
|
||||
want_effect: "Deny"
|
||||
|
||||
# =========================================================================
|
||||
# Tables: throughputSettings autoscale (provisionedThroughputSettings) → Deny
|
||||
# =========================================================================
|
||||
|
||||
- note: deny_table_autoscale
|
||||
parameters:
|
||||
throughputMax: 400
|
||||
resource:
|
||||
type: "Microsoft.DocumentDB/databaseAccounts/tables"
|
||||
name: "test-table"
|
||||
properties:
|
||||
"default":
|
||||
resource:
|
||||
provisionedThroughputSettings:
|
||||
maxThroughput: 4000
|
||||
want_effect: "Deny"
|
||||
|
||||
# =========================================================================
|
||||
# Container: empty options.throughput → evaluates to 0 → pass
|
||||
# =========================================================================
|
||||
|
||||
- note: pass_container_no_throughput
|
||||
parameters:
|
||||
throughputMax: 400
|
||||
resource:
|
||||
type: "Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers"
|
||||
name: "test-container-empty"
|
||||
properties:
|
||||
options:
|
||||
throughput: ""
|
||||
want_undefined: true
|
||||
|
||||
# =========================================================================
|
||||
# Cassandra Keyspace: options.throughput exceeds max → Deny
|
||||
# =========================================================================
|
||||
|
||||
- note: deny_cassandra_keyspace_throughput
|
||||
parameters:
|
||||
throughputMax: 400
|
||||
resource:
|
||||
type: "Microsoft.DocumentDB/databaseAccounts/cassandraKeyspaces"
|
||||
name: "test-cassandra-ks"
|
||||
properties:
|
||||
options:
|
||||
throughput: "600"
|
||||
want_effect: "Deny"
|
||||
|
||||
# =========================================================================
|
||||
# Cassandra Table: options.throughput exceeds max → Deny
|
||||
# =========================================================================
|
||||
|
||||
- note: deny_cassandra_table_throughput
|
||||
parameters:
|
||||
throughputMax: 400
|
||||
resource:
|
||||
type: "Microsoft.DocumentDB/databaseAccounts/cassandraKeyspaces/tables"
|
||||
name: "test-cassandra-table"
|
||||
properties:
|
||||
options:
|
||||
throughput: "600"
|
||||
want_effect: "Deny"
|
||||
|
||||
# =========================================================================
|
||||
# Gremlin Database: options.throughput exceeds max → Deny
|
||||
# =========================================================================
|
||||
|
||||
- note: deny_gremlin_database_throughput
|
||||
parameters:
|
||||
throughputMax: 400
|
||||
resource:
|
||||
type: "Microsoft.DocumentDB/databaseAccounts/gremlinDatabases"
|
||||
name: "test-gremlin-db"
|
||||
properties:
|
||||
options:
|
||||
throughput: "600"
|
||||
want_effect: "Deny"
|
||||
|
||||
# =========================================================================
|
||||
# Gremlin Graph: autoscale (ProvisionedThroughputSettings key) → Deny
|
||||
# =========================================================================
|
||||
|
||||
- note: deny_gremlin_graph_autoscale
|
||||
parameters:
|
||||
throughputMax: 400
|
||||
resource:
|
||||
type: "Microsoft.DocumentDB/databaseAccounts/gremlinDatabases/graphs"
|
||||
name: "test-gremlin-graph"
|
||||
properties:
|
||||
options:
|
||||
ProvisionedThroughputSettings:
|
||||
maxThroughput: 4000
|
||||
want_effect: "Deny"
|
||||
|
||||
# =========================================================================
|
||||
# MongoDB Collection: options.throughput exceeds max → Deny
|
||||
# =========================================================================
|
||||
|
||||
- note: deny_mongo_collection_throughput
|
||||
parameters:
|
||||
throughputMax: 400
|
||||
resource:
|
||||
type: "Microsoft.DocumentDB/databaseAccounts/mongodbDatabases/collections"
|
||||
name: "test-mongo-collection"
|
||||
properties:
|
||||
options:
|
||||
throughput: "600"
|
||||
want_effect: "Deny"
|
||||
118
tests/azure_policy/cases/e2e_cosmos_private_modify.yaml
Normal file
118
tests/azure_policy/cases/e2e_cosmos_private_modify.yaml
Normal file
@@ -0,0 +1,118 @@
|
||||
# Copyright (c) Microsoft Corporation.
|
||||
# Licensed under the MIT License.
|
||||
|
||||
# E2E Test: Cosmos DB/Cosmos_PrivateNetworkAccess_Modify
|
||||
# Real Azure Policy: "Configure CosmosDB accounts to disable public network access"
|
||||
# Source: regolator/policyDefinitions/Cosmos DB/Cosmos_PrivateNetworkAccess_Modify.json
|
||||
#
|
||||
# Features exercised:
|
||||
# - Modify effect with requestContext().apiVersion condition on operation
|
||||
# - greaterOrEquals on API version string
|
||||
# - conflictEffect in details
|
||||
|
||||
aliases: test_aliases.json
|
||||
|
||||
policy_definition: |
|
||||
{
|
||||
"properties": {
|
||||
"displayName": "Configure CosmosDB accounts to disable public network access",
|
||||
"policyType": "BuiltIn",
|
||||
"mode": "Indexed",
|
||||
"parameters": {
|
||||
"effect": {
|
||||
"type": "String",
|
||||
"allowedValues": ["Modify", "Disabled"],
|
||||
"defaultValue": "Modify"
|
||||
}
|
||||
},
|
||||
"policyRule": {
|
||||
"if": {
|
||||
"allOf": [
|
||||
{
|
||||
"field": "type",
|
||||
"equals": "Microsoft.DocumentDB/databaseAccounts"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.DocumentDB/databaseAccounts/publicNetworkAccess",
|
||||
"notEquals": "Disabled"
|
||||
}
|
||||
]
|
||||
},
|
||||
"then": {
|
||||
"effect": "[parameters('effect')]",
|
||||
"details": {
|
||||
"roleDefinitionIds": [
|
||||
"/providers/microsoft.authorization/roleDefinitions/b24988ac-6180-42a0-ab88-20f7382dd24c",
|
||||
"/providers/Microsoft.Authorization/roleDefinitions/5bd9cd88-fe45-4216-938b-f97437e15450"
|
||||
],
|
||||
"conflictEffect": "audit",
|
||||
"operations": [
|
||||
{
|
||||
"condition": "[greaterOrEquals(requestContext().apiVersion, '2021-01-15')]",
|
||||
"operation": "addOrReplace",
|
||||
"field": "Microsoft.DocumentDB/databaseAccounts/publicNetworkAccess",
|
||||
"value": "Disabled"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cases:
|
||||
# =========================================================================
|
||||
# Public access enabled → modify
|
||||
# =========================================================================
|
||||
|
||||
- note: modify_public_access_enabled
|
||||
resource:
|
||||
type: "Microsoft.DocumentDB/databaseAccounts"
|
||||
name: "cosmos-public"
|
||||
properties:
|
||||
publicNetworkAccess: "Enabled"
|
||||
api_version: "2023-04-15"
|
||||
want_effect: "Modify"
|
||||
want_details:
|
||||
roleDefinitionIds:
|
||||
- "/providers/microsoft.authorization/roleDefinitions/b24988ac-6180-42a0-ab88-20f7382dd24c"
|
||||
- "/providers/Microsoft.Authorization/roleDefinitions/5bd9cd88-fe45-4216-938b-f97437e15450"
|
||||
operations:
|
||||
- condition: "[greaterOrEquals(requestContext().apiVersion, '2021-01-15')]"
|
||||
operation: "addOrReplace"
|
||||
field: "Microsoft.DocumentDB/databaseAccounts/publicNetworkAccess"
|
||||
value: "Disabled"
|
||||
|
||||
- note: modify_public_access_missing
|
||||
resource:
|
||||
type: "Microsoft.DocumentDB/databaseAccounts"
|
||||
name: "cosmos-no-field"
|
||||
properties: {}
|
||||
api_version: "2023-04-15"
|
||||
want_effect: "Modify"
|
||||
|
||||
# =========================================================================
|
||||
# Public access already disabled → pass
|
||||
# =========================================================================
|
||||
|
||||
- note: pass_public_access_disabled
|
||||
resource:
|
||||
type: "Microsoft.DocumentDB/databaseAccounts"
|
||||
name: "cosmos-disabled"
|
||||
properties:
|
||||
publicNetworkAccess: "Disabled"
|
||||
api_version: "2023-04-15"
|
||||
want_undefined: true
|
||||
|
||||
# =========================================================================
|
||||
# Wrong type
|
||||
# =========================================================================
|
||||
|
||||
- note: pass_wrong_type
|
||||
resource:
|
||||
type: "Microsoft.Storage/storageAccounts"
|
||||
name: "not-cosmos"
|
||||
properties:
|
||||
publicNetworkAccess: "Enabled"
|
||||
api_version: "2023-04-15"
|
||||
want_undefined: true
|
||||
231
tests/azure_policy/cases/e2e_custom_owner_role.yaml
Normal file
231
tests/azure_policy/cases/e2e_custom_owner_role.yaml
Normal file
@@ -0,0 +1,231 @@
|
||||
# Copyright (c) Microsoft Corporation.
|
||||
# Licensed under the MIT License.
|
||||
|
||||
# E2E Test: General/CustomSubscription_OwnerRole_Audit
|
||||
# Real Azure Policy: "[Deprecated]: Custom subscription owner roles should not exist"
|
||||
# Source: regolator/policyDefinitions/General/CustomSubscription_OwnerRole_Audit.json
|
||||
#
|
||||
# Features exercised:
|
||||
# - 4 double-negation blocks: not { field notEquals }, not { field notIn }, not { field notLike }
|
||||
# - Array wildcard aliases: permissions[*].actions[*], assignableScopes[*]
|
||||
# - subscription().id and concat(subscription().id, '/')
|
||||
# - notLike "/providers/Microsoft.Management/*"
|
||||
# - Deeply nested sub-resource arrays (permissions[*].actions[*])
|
||||
|
||||
aliases: test_aliases.json
|
||||
|
||||
policy_definition: |
|
||||
{
|
||||
"properties": {
|
||||
"displayName": "[Deprecated]: Custom subscription owner roles should not exist",
|
||||
"policyType": "BuiltIn",
|
||||
"mode": "All",
|
||||
"parameters": {
|
||||
"effect": {
|
||||
"type": "string",
|
||||
"defaultValue": "Audit",
|
||||
"allowedValues": ["Audit", "Disabled"]
|
||||
}
|
||||
},
|
||||
"policyRule": {
|
||||
"if": {
|
||||
"allOf": [
|
||||
{
|
||||
"field": "type",
|
||||
"equals": "Microsoft.Authorization/roleDefinitions"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.Authorization/roleDefinitions/type",
|
||||
"equals": "CustomRole"
|
||||
},
|
||||
{
|
||||
"anyOf": [
|
||||
{
|
||||
"not": {
|
||||
"field": "Microsoft.Authorization/roleDefinitions/permissions[*].actions[*]",
|
||||
"notEquals": "*"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"anyOf": [
|
||||
{
|
||||
"not": {
|
||||
"field": "Microsoft.Authorization/roleDefinitions/assignableScopes[*]",
|
||||
"notIn": [
|
||||
"[concat(subscription().id,'/')]",
|
||||
"[subscription().id]",
|
||||
"/"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"not": {
|
||||
"field": "Microsoft.Authorization/roleDefinitions/assignableScopes[*]",
|
||||
"notLike": "/providers/Microsoft.Management/*"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"then": {
|
||||
"effect": "[parameters('effect')]"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cases:
|
||||
# =========================================================================
|
||||
# Custom owner role with subscription scope → audit
|
||||
# =========================================================================
|
||||
|
||||
- note: audit_custom_owner_subscription_scope
|
||||
resource:
|
||||
type: "Microsoft.Authorization/roleDefinitions"
|
||||
name: "custom-owner"
|
||||
properties:
|
||||
type: "CustomRole"
|
||||
permissions:
|
||||
- actions:
|
||||
- "*"
|
||||
assignableScopes:
|
||||
- "/subscriptions/sub-123"
|
||||
context:
|
||||
subscription:
|
||||
subscriptionId: "sub-123"
|
||||
id: "/subscriptions/sub-123"
|
||||
want_effect: "Audit"
|
||||
|
||||
- note: audit_custom_owner_subscription_trailing_slash
|
||||
resource:
|
||||
type: "Microsoft.Authorization/roleDefinitions"
|
||||
name: "custom-owner-slash"
|
||||
properties:
|
||||
type: "CustomRole"
|
||||
permissions:
|
||||
- actions:
|
||||
- "*"
|
||||
assignableScopes:
|
||||
- "/subscriptions/sub-123/"
|
||||
context:
|
||||
subscription:
|
||||
subscriptionId: "sub-123"
|
||||
id: "/subscriptions/sub-123"
|
||||
want_effect: "Audit"
|
||||
|
||||
- note: audit_custom_owner_root_scope
|
||||
resource:
|
||||
type: "Microsoft.Authorization/roleDefinitions"
|
||||
name: "custom-owner-root"
|
||||
properties:
|
||||
type: "CustomRole"
|
||||
permissions:
|
||||
- actions:
|
||||
- "*"
|
||||
assignableScopes:
|
||||
- "/"
|
||||
context:
|
||||
subscription:
|
||||
subscriptionId: "sub-123"
|
||||
id: "/subscriptions/sub-123"
|
||||
want_effect: "Audit"
|
||||
|
||||
- note: audit_custom_owner_management_group_scope
|
||||
resource:
|
||||
type: "Microsoft.Authorization/roleDefinitions"
|
||||
name: "custom-owner-mg"
|
||||
properties:
|
||||
type: "CustomRole"
|
||||
permissions:
|
||||
- actions:
|
||||
- "*"
|
||||
assignableScopes:
|
||||
- "/providers/Microsoft.Management/managementGroups/mg1"
|
||||
context:
|
||||
subscription:
|
||||
subscriptionId: "sub-123"
|
||||
id: "/subscriptions/sub-123"
|
||||
want_effect: "Audit"
|
||||
|
||||
# =========================================================================
|
||||
# Custom role without owner (*) actions → pass
|
||||
# =========================================================================
|
||||
|
||||
- note: pass_custom_role_no_wildcard_actions
|
||||
resource:
|
||||
type: "Microsoft.Authorization/roleDefinitions"
|
||||
name: "custom-reader"
|
||||
properties:
|
||||
type: "CustomRole"
|
||||
permissions:
|
||||
- actions:
|
||||
- "Microsoft.Compute/virtualMachines/read"
|
||||
- "Microsoft.Storage/storageAccounts/read"
|
||||
assignableScopes:
|
||||
- "/subscriptions/sub-123"
|
||||
context:
|
||||
subscription:
|
||||
subscriptionId: "sub-123"
|
||||
id: "/subscriptions/sub-123"
|
||||
want_undefined: true
|
||||
|
||||
# =========================================================================
|
||||
# Custom owner role but NOT scoped to subscription/root/MG → pass
|
||||
# =========================================================================
|
||||
|
||||
- note: pass_custom_owner_resource_group_scope
|
||||
resource:
|
||||
type: "Microsoft.Authorization/roleDefinitions"
|
||||
name: "custom-owner-rg"
|
||||
properties:
|
||||
type: "CustomRole"
|
||||
permissions:
|
||||
- actions:
|
||||
- "*"
|
||||
assignableScopes:
|
||||
- "/subscriptions/sub-123/resourceGroups/rg1"
|
||||
context:
|
||||
subscription:
|
||||
subscriptionId: "sub-123"
|
||||
id: "/subscriptions/sub-123"
|
||||
want_undefined: true
|
||||
|
||||
# =========================================================================
|
||||
# BuiltIn role (not CustomRole) → pass
|
||||
# =========================================================================
|
||||
|
||||
- note: pass_builtin_role
|
||||
resource:
|
||||
type: "Microsoft.Authorization/roleDefinitions"
|
||||
name: "builtin-owner"
|
||||
properties:
|
||||
type: "BuiltInRole"
|
||||
permissions:
|
||||
- actions:
|
||||
- "*"
|
||||
assignableScopes:
|
||||
- "/"
|
||||
context:
|
||||
subscription:
|
||||
subscriptionId: "sub-123"
|
||||
id: "/subscriptions/sub-123"
|
||||
want_undefined: true
|
||||
|
||||
# =========================================================================
|
||||
# Wrong resource type
|
||||
# =========================================================================
|
||||
|
||||
- note: pass_wrong_type
|
||||
resource:
|
||||
type: "Microsoft.Compute/virtualMachines"
|
||||
name: "not-role-def"
|
||||
properties:
|
||||
type: "CustomRole"
|
||||
context:
|
||||
subscription:
|
||||
subscriptionId: "sub-123"
|
||||
id: "/subscriptions/sub-123"
|
||||
want_undefined: true
|
||||
458
tests/azure_policy/cases/e2e_datafactory_linked_secrets.yaml
Normal file
458
tests/azure_policy/cases/e2e_datafactory_linked_secrets.yaml
Normal file
@@ -0,0 +1,458 @@
|
||||
# Copyright (c) Microsoft Corporation.
|
||||
# Licensed under the MIT License.
|
||||
|
||||
# E2E Test: Data Factory/LinkedService_InlineSecrets_Audit
|
||||
# Real Azure Policy: "Azure Data Factory linked services should use Key Vault for storing secrets"
|
||||
# Source: regolator/policyDefinitions/Data Factory/LinkedService_InlineSecrets_Audit.json
|
||||
#
|
||||
# Features exercised:
|
||||
# - Large anyOf with 20+ branches
|
||||
# - "contains" operator for secret keywords in connectionString
|
||||
# - "exists" checks for secret fields
|
||||
# - "equals" / "notEquals" / "in" checks for .type field (SecureString vs AzureKeyVaultSecret)
|
||||
# - Service-type-prefixed aliases (SqlServer., AzureStorage., etc.)
|
||||
|
||||
aliases: test_aliases.json
|
||||
|
||||
policy_definition: |
|
||||
{
|
||||
"properties": {
|
||||
"displayName": "Azure Data Factory linked services should use Key Vault for storing secrets",
|
||||
"policyType": "BuiltIn",
|
||||
"mode": "All",
|
||||
"parameters": {
|
||||
"effect": {
|
||||
"type": "String",
|
||||
"allowedValues": ["Audit", "Deny", "Disabled"],
|
||||
"defaultValue": "Audit"
|
||||
}
|
||||
},
|
||||
"policyRule": {
|
||||
"if": {
|
||||
"allOf": [
|
||||
{
|
||||
"field": "type",
|
||||
"equals": "Microsoft.DataFactory/factories/linkedservices"
|
||||
},
|
||||
{
|
||||
"anyOf": [
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"field": "Microsoft.DataFactory/factories/linkedservices/typeProperties.connectionString",
|
||||
"exists": "true"
|
||||
},
|
||||
{
|
||||
"anyOf": [
|
||||
{
|
||||
"field": "Microsoft.DataFactory/factories/linkedservices/typeProperties.connectionString",
|
||||
"contains": "AccountKey="
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.DataFactory/factories/linkedservices/typeProperties.connectionString",
|
||||
"contains": "PWD="
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.DataFactory/factories/linkedservices/typeProperties.connectionString",
|
||||
"contains": "Password="
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.DataFactory/factories/linkedservices/typeProperties.connectionString",
|
||||
"contains": "CredString="
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.DataFactory/factories/linkedservices/typeProperties.connectionString",
|
||||
"contains": "pwd="
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.DataFactory/factories/linkedservices/SqlServer.typeProperties.password.type",
|
||||
"equals": "SecureString"
|
||||
},
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"field": "Microsoft.DataFactory/factories/linkedservices/SqlServer.typeProperties.password",
|
||||
"exists": "true"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.DataFactory/factories/linkedservices/SqlServer.typeProperties.password.type",
|
||||
"exists": "false"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.DataFactory/factories/linkedservices/AzureSqlDW.typeProperties.servicePrincipalKey.type",
|
||||
"equals": "SecureString"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.DataFactory/factories/linkedservices/AzureSearch.typeProperties.key.type",
|
||||
"equals": "SecureString"
|
||||
},
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"field": "Microsoft.DataFactory/factories/linkedservices/AzureStorage.typeProperties.sasUri",
|
||||
"exists": "true"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.DataFactory/factories/linkedservices/AzureStorage.typeProperties.sasUri.type",
|
||||
"notEquals": "AzureKeyVaultSecret"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"field": "Microsoft.DataFactory/factories/linkedservices/AzureBlobStorage.typeProperties.servicePrincipalKey",
|
||||
"exists": "true"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.DataFactory/factories/linkedservices/AzureBlobStorage.typeProperties.servicePrincipalKey.type",
|
||||
"notEquals": "AzureKeyVaultSecret"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"field": "Microsoft.DataFactory/factories/linkedservices/AzureStorage.typeProperties.accountKey",
|
||||
"exists": "true"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.DataFactory/factories/linkedservices/CosmosDb.typeProperties.accountKey.type",
|
||||
"notEquals": "AzureKeyVaultSecret"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.DataFactory/factories/linkedservices/typeProperties.encryptedCredential",
|
||||
"exists": "true"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.DataFactory/factories/linkedservices/AmazonMWS.typeProperties.mwsAuthToken.type",
|
||||
"equals": "SecureString"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.DataFactory/factories/linkedservices/AmazonMWS.typeProperties.secretKey.type",
|
||||
"equals": "SecureString"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.DataFactory/factories/linkedservices/AmazonS3.typeProperties.secretAccessKey.type",
|
||||
"equals": "SecureString"
|
||||
},
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"field": "Microsoft.DataFactory/factories/linkedservices/Dynamics.typeProperties.servicePrincipalCredential",
|
||||
"exists": "true"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.DataFactory/factories/linkedservices/Dynamics.typeProperties.servicePrincipalCredential.type",
|
||||
"equals": "SecureString"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"field": "Microsoft.DataFactory/factories/linkedservices/Hubspot.typeProperties.accessToken",
|
||||
"exists": "true"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.DataFactory/factories/linkedservices/Hubspot.typeProperties.accessToken.type",
|
||||
"equals": "SecureString"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.DataFactory/factories/linkedservices/Odbc.typeProperties.credential.type",
|
||||
"equals": "SecureString"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.DataFactory/factories/linkedservices/GoogleAdWords.typeProperties.developerToken.type",
|
||||
"equals": "SecureString"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.DataFactory/factories/linkedservices/GoogleBigQuery.typeProperties.clientSecret.type",
|
||||
"equals": "SecureString"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.DataFactory/factories/linkedservices/GoogleBigQuery.typeProperties.refreshToken.type",
|
||||
"equals": "SecureString"
|
||||
},
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"field": "Microsoft.DataFactory/factories/linkedservices/type",
|
||||
"in": [
|
||||
"MongoDbAtlas",
|
||||
"MongoDbV2"
|
||||
]
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.DataFactory/factories/linkedservices/typeProperties.connectionString.type",
|
||||
"notEquals": "AzureKeyVaultSecret"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.DataFactory/factories/linkedservices/OData.typeProperties.servicePrincipalEmbeddedCert.type",
|
||||
"equals": "SecureString"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.DataFactory/factories/linkedservices/OData.typeProperties.servicePrincipalEmbeddedCertPassword.type",
|
||||
"equals": "SecureString"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.DataFactory/factories/linkedservices/Sftp.typeProperties.privateKeyContent.type",
|
||||
"equals": "SecureString"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.DataFactory/factories/linkedservices/Sftp.typeProperties.passPhrase.type",
|
||||
"equals": "SecureString"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.DataFactory/factories/linkedservices/Salesforce.typeProperties.securityToken.type",
|
||||
"equals": "SecureString"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"then": {
|
||||
"effect": "[parameters('effect')]"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cases:
|
||||
# =========================================================================
|
||||
# connectionString contains "Password=" → Audit
|
||||
# =========================================================================
|
||||
|
||||
- note: audit_connection_string_with_password
|
||||
resource:
|
||||
type: "Microsoft.DataFactory/factories/linkedservices"
|
||||
name: "adf-sql-inline-password"
|
||||
properties:
|
||||
type: SqlServer
|
||||
typeProperties:
|
||||
connectionString: "Server=myserver.database.windows.net;Database=mydb;User ID=admin;Password=secret123"
|
||||
want_effect: "Audit"
|
||||
|
||||
# =========================================================================
|
||||
# connectionString without secret keywords → pass
|
||||
# =========================================================================
|
||||
|
||||
- note: pass_connection_string_no_secrets
|
||||
resource:
|
||||
type: "Microsoft.DataFactory/factories/linkedservices"
|
||||
name: "adf-sql-integrated"
|
||||
properties:
|
||||
type: SqlServer
|
||||
typeProperties:
|
||||
connectionString: "Server=myserver.database.windows.net;Database=mydb;Integrated Security=true"
|
||||
want_undefined: true
|
||||
|
||||
# =========================================================================
|
||||
# SqlServer password.type = SecureString → Audit
|
||||
# =========================================================================
|
||||
|
||||
- note: audit_sql_server_secure_string
|
||||
resource:
|
||||
type: "Microsoft.DataFactory/factories/linkedservices"
|
||||
name: "adf-sql-securestring"
|
||||
properties:
|
||||
type: SqlServer
|
||||
typeProperties:
|
||||
connectionString: "Server=myserver.database.windows.net;Database=mydb;Integrated Security=true"
|
||||
password:
|
||||
type: SecureString
|
||||
value: "my-password"
|
||||
want_effect: "Audit"
|
||||
|
||||
# =========================================================================
|
||||
# SqlServer password.type = AzureKeyVaultSecret → pass
|
||||
# =========================================================================
|
||||
|
||||
- note: pass_sql_server_keyvault
|
||||
resource:
|
||||
type: "Microsoft.DataFactory/factories/linkedservices"
|
||||
name: "adf-sql-keyvault"
|
||||
properties:
|
||||
type: SqlServer
|
||||
typeProperties:
|
||||
connectionString: "Server=myserver.database.windows.net;Database=mydb;Integrated Security=true"
|
||||
password:
|
||||
type: AzureKeyVaultSecret
|
||||
store:
|
||||
referenceName: myKeyVault
|
||||
type: LinkedServiceReference
|
||||
secretName: mySecret
|
||||
want_undefined: true
|
||||
|
||||
# =========================================================================
|
||||
# encryptedCredential exists → Audit
|
||||
# =========================================================================
|
||||
|
||||
- note: audit_encrypted_credential
|
||||
resource:
|
||||
type: "Microsoft.DataFactory/factories/linkedservices"
|
||||
name: "adf-encrypted"
|
||||
properties:
|
||||
type: AzureBlobStorage
|
||||
typeProperties:
|
||||
connectionString: "DefaultEndpointsProtocol=https;EndpointSuffix=core.windows.net"
|
||||
encryptedCredential: "eyJWZXJzaW9uIj..."
|
||||
want_effect: "Audit"
|
||||
|
||||
# =========================================================================
|
||||
# No secrets at all → pass
|
||||
# =========================================================================
|
||||
|
||||
- note: pass_no_secrets
|
||||
resource:
|
||||
type: "Microsoft.DataFactory/factories/linkedservices"
|
||||
name: "adf-clean"
|
||||
properties:
|
||||
type: AzureBlobFS
|
||||
typeProperties:
|
||||
url: "https://mydatalake.dfs.core.windows.net"
|
||||
want_undefined: true
|
||||
|
||||
# =========================================================================
|
||||
# Wrong resource type → pass
|
||||
# =========================================================================
|
||||
|
||||
- note: pass_wrong_type
|
||||
resource:
|
||||
type: "Microsoft.Storage/storageAccounts"
|
||||
name: "not-data-factory"
|
||||
properties:
|
||||
supportsHttpsTrafficOnly: true
|
||||
want_undefined: true
|
||||
|
||||
# =========================================================================
|
||||
# AzureStorage sasUri exists but type not AzureKeyVaultSecret → Audit
|
||||
# =========================================================================
|
||||
|
||||
- note: audit_storage_sas_no_keyvault
|
||||
resource:
|
||||
type: "Microsoft.DataFactory/factories/linkedservices"
|
||||
name: "adf-storage-inline-sas"
|
||||
properties:
|
||||
type: AzureStorage
|
||||
typeProperties:
|
||||
sasUri: "https://mystorage.blob.core.windows.net/?sv=2020-08-04&ss=b&srt=sco&sp=rwdlacupx"
|
||||
want_effect: "Audit"
|
||||
|
||||
# =========================================================================
|
||||
# AzureSqlDW servicePrincipalKey.type = SecureString → Audit
|
||||
# =========================================================================
|
||||
|
||||
- note: audit_azure_sql_dw_spkey_securestring
|
||||
resource:
|
||||
type: "Microsoft.DataFactory/factories/linkedservices"
|
||||
name: "adf-sqldw-spkey"
|
||||
properties:
|
||||
type: AzureSqlDW
|
||||
typeProperties:
|
||||
connectionString: "Server=myserver.database.windows.net;Database=mydb;Integrated Security=true"
|
||||
servicePrincipalKey:
|
||||
type: SecureString
|
||||
value: "my-sp-key"
|
||||
want_effect: "Audit"
|
||||
|
||||
# =========================================================================
|
||||
# AzureStorage accountKey exists + type != AzureKeyVaultSecret → Audit
|
||||
# =========================================================================
|
||||
|
||||
- note: audit_storage_accountkey_not_keyvault
|
||||
resource:
|
||||
type: "Microsoft.DataFactory/factories/linkedservices"
|
||||
name: "adf-storage-accountkey"
|
||||
properties:
|
||||
type: AzureStorage
|
||||
typeProperties:
|
||||
connectionString: "DefaultEndpointsProtocol=https;EndpointSuffix=core.windows.net"
|
||||
accountKey:
|
||||
type: SecureString
|
||||
value: "base64accountkey=="
|
||||
want_effect: "Audit"
|
||||
|
||||
# =========================================================================
|
||||
# MongoDbAtlas connectionString.type != AzureKeyVaultSecret → Audit
|
||||
# =========================================================================
|
||||
|
||||
- note: audit_mongodbatlas_connstr_not_keyvault
|
||||
resource:
|
||||
type: "Microsoft.DataFactory/factories/linkedservices"
|
||||
name: "adf-mongodbatlas-inline"
|
||||
properties:
|
||||
type: MongoDbAtlas
|
||||
typeProperties:
|
||||
connectionString:
|
||||
type: SecureString
|
||||
value: "mongodb+srv://user:pass@cluster0.mongodb.net/mydb"
|
||||
database: mydb
|
||||
want_effect: "Audit"
|
||||
|
||||
# =========================================================================
|
||||
# AmazonS3 secretAccessKey.type = SecureString → Audit
|
||||
# =========================================================================
|
||||
|
||||
- note: audit_amazon_s3_secret_securestring
|
||||
resource:
|
||||
type: "Microsoft.DataFactory/factories/linkedservices"
|
||||
name: "adf-amazons3-secret"
|
||||
properties:
|
||||
type: AmazonS3
|
||||
typeProperties:
|
||||
accessKeyId: "AKIAIOSFODNN7EXAMPLE"
|
||||
secretAccessKey:
|
||||
type: SecureString
|
||||
value: "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"
|
||||
want_effect: "Audit"
|
||||
|
||||
# =========================================================================
|
||||
# Sftp privateKeyContent.type = SecureString → Audit
|
||||
# =========================================================================
|
||||
|
||||
- note: audit_sftp_privatekey_securestring
|
||||
resource:
|
||||
type: "Microsoft.DataFactory/factories/linkedservices"
|
||||
name: "adf-sftp-privatekey"
|
||||
properties:
|
||||
type: Sftp
|
||||
typeProperties:
|
||||
host: "sftp.example.com"
|
||||
userName: "sftpuser"
|
||||
privateKeyContent:
|
||||
type: SecureString
|
||||
value: "-----BEGIN RSA PRIVATE KEY-----..."
|
||||
want_effect: "Audit"
|
||||
|
||||
# =========================================================================
|
||||
# Odbc credential.type = AzureKeyVaultSecret → pass (not SecureString)
|
||||
# =========================================================================
|
||||
|
||||
- note: pass_odbc_credential_keyvault
|
||||
resource:
|
||||
type: "Microsoft.DataFactory/factories/linkedservices"
|
||||
name: "adf-odbc-keyvault"
|
||||
properties:
|
||||
type: Odbc
|
||||
typeProperties:
|
||||
connectionString: "Driver={SQL Server};Server=myserver;Database=mydb"
|
||||
credential:
|
||||
type: AzureKeyVaultSecret
|
||||
store:
|
||||
referenceName: myKeyVault
|
||||
type: LinkedServiceReference
|
||||
secretName: odbcCredential
|
||||
want_undefined: true
|
||||
757
tests/azure_policy/cases/e2e_dcra_vmss_linux_dine.yaml
Normal file
757
tests/azure_policy/cases/e2e_dcra_vmss_linux_dine.yaml
Normal file
@@ -0,0 +1,757 @@
|
||||
# Copyright (c) Microsoft Corporation.
|
||||
# Licensed under the MIT License.
|
||||
|
||||
# E2E Test: Monitoring/AzureMonitor_DCRA_VMSS_Linux_DINE
|
||||
# Real Azure Policy: "Configure Linux Virtual Machine Scale Sets to be associated
|
||||
# with a Data Collection Rule or a Data Collection Endpoint"
|
||||
# Source: regolator/policyDefinitions/Monitoring/AzureMonitor_DCRA_VMSS_Linux_DINE.json
|
||||
#
|
||||
# Features exercised:
|
||||
# - 78 condition nodes, 611 lines — representative of ~30 Monitoring/* policies
|
||||
# - Boolean parameter (scopeToSupportedImages)
|
||||
# - Polymorphic resourceType parameter (DCR vs DCE)
|
||||
# - DeployIfNotExists with conditional deployment
|
||||
# - existenceCondition with anyOf
|
||||
# - Large hardcoded region list (60+ locations)
|
||||
|
||||
aliases: test_aliases.json
|
||||
|
||||
policy_definition: |
|
||||
{
|
||||
"properties": {
|
||||
"displayName": "Configure Linux Virtual Machine Scale Sets to be associated with a Data Collection Rule or a Data Collection Endpoint",
|
||||
"policyType": "BuiltIn",
|
||||
"mode": "Indexed",
|
||||
"description": "Deploy Association to link Linux virtual machine scale sets to the specified Data Collection Rule or the specified Data Collection Endpoint. The list of locations and OS images are updated over time as support is increased.",
|
||||
"parameters": {
|
||||
"effect": {
|
||||
"type": "String",
|
||||
"metadata": {
|
||||
"displayName": "Effect",
|
||||
"description": "Enable or disable the execution of the policy."
|
||||
},
|
||||
"allowedValues": [
|
||||
"DeployIfNotExists",
|
||||
"Disabled"
|
||||
],
|
||||
"defaultValue": "DeployIfNotExists"
|
||||
},
|
||||
"scopeToSupportedImages": {
|
||||
"type": "Boolean",
|
||||
"metadata": {
|
||||
"displayName": "Scope Policy to Azure Monitor Agent-Supported Operating Systems",
|
||||
"description": "If set to true, the policy will apply only to virtual machine scale sets with AMA-supported operating systems. Otherwise, the policy will apply to all virtual machine scale set resources in the assignment scope. For supported operating systems, see https://aka.ms/AMAOverview."
|
||||
},
|
||||
"allowedValues": [
|
||||
true,
|
||||
false
|
||||
],
|
||||
"defaultValue": true
|
||||
},
|
||||
"listOfLinuxImageIdToInclude": {
|
||||
"type": "Array",
|
||||
"metadata": {
|
||||
"displayName": "Additional Linux Machine Images",
|
||||
"description": "List of virtual machine scale set images that have supported Linux OS to add to scope. Example values: '/subscriptions/<subscriptionId>/resourceGroups/YourResourceGroup/providers/Microsoft.Compute/images/ContosoStdImage'"
|
||||
},
|
||||
"defaultValue": []
|
||||
},
|
||||
"dcrResourceId": {
|
||||
"type": "String",
|
||||
"metadata": {
|
||||
"displayName": "Data Collection Rule Resource Id or Data Collection Endpoint Resource Id",
|
||||
"description": "Resource Id of the Data Collection Rule or the Data Collection Endpoint to be applied on the Linux machines in scope.",
|
||||
"portalReview": "true",
|
||||
"assignPermissions": true
|
||||
}
|
||||
},
|
||||
"resourceType": {
|
||||
"type": "String",
|
||||
"metadata": {
|
||||
"displayName": "Resource Type",
|
||||
"description": "Either a Data Collection Rule (DCR) or a Data Collection Endpoint (DCE)",
|
||||
"portalReview": "true"
|
||||
},
|
||||
"allowedValues": [
|
||||
"Microsoft.Insights/dataCollectionRules",
|
||||
"Microsoft.Insights/dataCollectionEndpoints"
|
||||
],
|
||||
"defaultValue": "Microsoft.Insights/dataCollectionRules"
|
||||
}
|
||||
},
|
||||
"policyRule": {
|
||||
"if": {
|
||||
"allOf": [
|
||||
{
|
||||
"field": "type",
|
||||
"equals": "Microsoft.Compute/virtualMachineScaleSets"
|
||||
},
|
||||
{
|
||||
"field": "location",
|
||||
"in": [
|
||||
"australiacentral",
|
||||
"australiacentral2",
|
||||
"australiaeast",
|
||||
"australiasoutheast",
|
||||
"brazilsouth",
|
||||
"brazilsoutheast",
|
||||
"canadacentral",
|
||||
"canadaeast",
|
||||
"centralindia",
|
||||
"centralus",
|
||||
"centraluseuap",
|
||||
"eastasia",
|
||||
"eastus",
|
||||
"eastus2",
|
||||
"eastus2euap",
|
||||
"francecentral",
|
||||
"francesouth",
|
||||
"germanynorth",
|
||||
"germanywestcentral",
|
||||
"israelcentral",
|
||||
"italynorth",
|
||||
"japaneast",
|
||||
"japanwest",
|
||||
"jioindiacentral",
|
||||
"jioindiawest",
|
||||
"koreacentral",
|
||||
"koreasouth",
|
||||
"malaysiasouth",
|
||||
"mexicocentral",
|
||||
"northcentralus",
|
||||
"northeurope",
|
||||
"norwayeast",
|
||||
"norwaywest",
|
||||
"polandcentral",
|
||||
"qatarcentral",
|
||||
"southafricanorth",
|
||||
"southafricawest",
|
||||
"southcentralus",
|
||||
"southeastasia",
|
||||
"southindia",
|
||||
"spaincentral",
|
||||
"swedencentral",
|
||||
"swedensouth",
|
||||
"switzerlandnorth",
|
||||
"switzerlandwest",
|
||||
"taiwannorth",
|
||||
"taiwannorthwest",
|
||||
"uaecentral",
|
||||
"uaenorth",
|
||||
"uksouth",
|
||||
"ukwest",
|
||||
"westcentralus",
|
||||
"westeurope",
|
||||
"westindia",
|
||||
"westus",
|
||||
"westus2",
|
||||
"westus3"
|
||||
]
|
||||
},
|
||||
{
|
||||
"anyOf": [
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"value": "[parameters('scopeToSupportedImages')]",
|
||||
"equals": false
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.Compute/virtualMachineScaleSets/virtualMachineProfile.storageProfile.osDisk.osType",
|
||||
"like": "Linux*"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.Compute/imageId",
|
||||
"in": "[parameters('listOfLinuxImageIdToInclude')]"
|
||||
},
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"field": "Microsoft.Compute/imagePublisher",
|
||||
"equals": "RedHat"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.Compute/imageOffer",
|
||||
"in": [
|
||||
"RHEL",
|
||||
"RHEL-ARM64",
|
||||
"RHEL-BYOS",
|
||||
"RHEL-HA",
|
||||
"RHEL-SAP",
|
||||
"RHEL-SAP-APPS",
|
||||
"RHEL-SAP-HA"
|
||||
]
|
||||
},
|
||||
{
|
||||
"anyOf": [
|
||||
{
|
||||
"field": "Microsoft.Compute/imageSku",
|
||||
"like": "7*"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.Compute/imageSku",
|
||||
"like": "8*"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.Compute/imageSku",
|
||||
"like": "9*"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.Compute/imageSku",
|
||||
"like": "rhel-lvm7*"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.Compute/imageSku",
|
||||
"like": "rhel-lvm8*"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.Compute/imageSku",
|
||||
"like": "rhel-lvm9*"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"field": "Microsoft.Compute/imagePublisher",
|
||||
"equals": "SUSE"
|
||||
},
|
||||
{
|
||||
"anyOf": [
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"field": "Microsoft.Compute/imageOffer",
|
||||
"in": [
|
||||
"SLES",
|
||||
"SLES-HPC",
|
||||
"SLES-HPC-Priority",
|
||||
"SLES-SAP",
|
||||
"SLES-SAP-BYOS",
|
||||
"SLES-Priority",
|
||||
"SLES-BYOS",
|
||||
"SLES-SAPCAL",
|
||||
"SLES-Standard"
|
||||
]
|
||||
},
|
||||
{
|
||||
"anyOf": [
|
||||
{
|
||||
"field": "Microsoft.Compute/imageSku",
|
||||
"like": "12*"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.Compute/imageSku",
|
||||
"like": "15*"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"anyOf": [
|
||||
{
|
||||
"field": "Microsoft.Compute/imageOffer",
|
||||
"like": "sles-12*"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.Compute/imageOffer",
|
||||
"like": "sles-15*"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.Compute/imageSku",
|
||||
"in": [
|
||||
"gen1",
|
||||
"gen2"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"field": "Microsoft.Compute/imagePublisher",
|
||||
"equals": "Canonical"
|
||||
},
|
||||
{
|
||||
"anyOf": [
|
||||
{
|
||||
"field": "Microsoft.Compute/imageOffer",
|
||||
"equals": "UbuntuServer"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.Compute/imageOffer",
|
||||
"like": "0001-com-ubuntu-server-*"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.Compute/imageOffer",
|
||||
"like": "0001-com-ubuntu-pro-*"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.Compute/imageSku",
|
||||
"in": [
|
||||
"14.04.0-lts",
|
||||
"14.04.1-lts",
|
||||
"14.04.2-lts",
|
||||
"14.04.3-lts",
|
||||
"14.04.4-lts",
|
||||
"14.04.5-lts",
|
||||
"16_04_0-lts-gen2",
|
||||
"16_04-lts-gen2",
|
||||
"16.04-lts",
|
||||
"16.04.0-lts",
|
||||
"18_04-lts-arm64",
|
||||
"18_04-lts-gen2",
|
||||
"18.04-lts",
|
||||
"20_04-lts-arm64",
|
||||
"20_04-lts-gen2",
|
||||
"20_04-lts",
|
||||
"22_04-lts-gen2",
|
||||
"22_04-lts",
|
||||
"pro-16_04-lts-gen2",
|
||||
"pro-16_04-lts",
|
||||
"pro-18_04-lts-gen2",
|
||||
"pro-18_04-lts",
|
||||
"pro-20_04-lts-gen2",
|
||||
"pro-20_04-lts",
|
||||
"pro-22_04-lts-gen2",
|
||||
"pro-22_04-lts"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"field": "Microsoft.Compute/imagePublisher",
|
||||
"equals": "Oracle"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.Compute/imageOffer",
|
||||
"equals": "Oracle-Linux"
|
||||
},
|
||||
{
|
||||
"anyOf": [
|
||||
{
|
||||
"field": "Microsoft.Compute/imageSku",
|
||||
"like": "7*"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.Compute/imageSku",
|
||||
"like": "8*"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.Compute/imageSku",
|
||||
"like": "ol7*"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.Compute/imageSku",
|
||||
"like": "ol8*"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.Compute/imageSku",
|
||||
"like": "ol9*"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"field": "Microsoft.Compute/imagePublisher",
|
||||
"equals": "OpenLogic"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.Compute/imageOffer",
|
||||
"in": [
|
||||
"CentOS",
|
||||
"Centos-LVM",
|
||||
"CentOS-SRIOV"
|
||||
]
|
||||
},
|
||||
{
|
||||
"anyOf": [
|
||||
{
|
||||
"field": "Microsoft.Compute/imageSku",
|
||||
"like": "7*"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.Compute/imageSku",
|
||||
"like": "8*"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"field": "Microsoft.Compute/imagePublisher",
|
||||
"equals": "cloudera"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.Compute/imageOffer",
|
||||
"equals": "cloudera-centos-os"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.Compute/imageSku",
|
||||
"like": "7*"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"field": "Microsoft.Compute/imagePublisher",
|
||||
"equals": "almalinux"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.Compute/imageOffer",
|
||||
"like": "almalinux*"
|
||||
},
|
||||
{
|
||||
"anyOf": [
|
||||
{
|
||||
"field": "Microsoft.Compute/imageSku",
|
||||
"like": "8*"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.Compute/imageSku",
|
||||
"like": "9*"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"field": "Microsoft.Compute/imagePublisher",
|
||||
"equals": "ctrliqinc1648673227698"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.Compute/imageOffer",
|
||||
"like": "rocky-8*"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.Compute/imageSku",
|
||||
"like": "rocky-8*"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"field": "Microsoft.Compute/imagePublisher",
|
||||
"equals": "credativ"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.Compute/imageOffer",
|
||||
"in": [
|
||||
"Debian"
|
||||
]
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.Compute/imageSku",
|
||||
"equals": "9"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"field": "Microsoft.Compute/imagePublisher",
|
||||
"equals": "Debian"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.Compute/imageOffer",
|
||||
"in": [
|
||||
"debian-10",
|
||||
"debian-11"
|
||||
]
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.Compute/imageSku",
|
||||
"in": [
|
||||
"10",
|
||||
"10-gen2",
|
||||
"11",
|
||||
"11-gen2"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"field": "Microsoft.Compute/imagePublisher",
|
||||
"equals": "microsoftcblmariner"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.Compute/imageOffer",
|
||||
"equals": "cbl-mariner"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.Compute/imageSku",
|
||||
"in": [
|
||||
"1-gen2",
|
||||
"cbl-mariner-1",
|
||||
"cbl-mariner-2",
|
||||
"cbl-mariner-2-arm64",
|
||||
"cbl-mariner-2-gen2"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"then": {
|
||||
"effect": "[parameters('effect')]",
|
||||
"details": {
|
||||
"type": "Microsoft.Insights/dataCollectionRuleAssociations",
|
||||
"roleDefinitionIds": [
|
||||
"/providers/microsoft.authorization/roleDefinitions/749f88d5-cbae-40b8-bcfc-e573ddc772fa",
|
||||
"/providers/microsoft.authorization/roleDefinitions/92aaf0da-9dab-42b6-94a3-d43ce8d16293"
|
||||
],
|
||||
"evaluationDelay": "AfterProvisioning",
|
||||
"existenceCondition": {
|
||||
"anyOf": [
|
||||
{
|
||||
"field": "Microsoft.Insights/dataCollectionRuleAssociations/dataCollectionRuleId",
|
||||
"equals": "[parameters('dcrResourceId')]"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.Insights/dataCollectionRuleAssociations/dataCollectionEndpointId",
|
||||
"equals": "[parameters('dcrResourceId')]"
|
||||
}
|
||||
]
|
||||
},
|
||||
"deployment": {
|
||||
"properties": {
|
||||
"mode": "incremental",
|
||||
"template": {
|
||||
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
|
||||
"contentVersion": "1.0.0.0",
|
||||
"parameters": {
|
||||
"resourceName": {
|
||||
"type": "string"
|
||||
},
|
||||
"location": {
|
||||
"type": "string"
|
||||
},
|
||||
"dcrResourceId": {
|
||||
"type": "string"
|
||||
},
|
||||
"resourceType": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"variables": {
|
||||
"dcrAssociationName": "[concat('assoc-', uniqueString(concat(parameters('resourceName'), parameters('dcrResourceId'))))]",
|
||||
"dceAssociationName": "configurationAccessEndpoint",
|
||||
"dcrResourceType": "Microsoft.Insights/dataCollectionRules",
|
||||
"dceResourceType": "Microsoft.Insights/dataCollectionEndpoints"
|
||||
},
|
||||
"resources": [
|
||||
{
|
||||
"condition": "[equals(parameters('resourceType'), variables('dcrResourceType'))]",
|
||||
"name": "[variables('dcrAssociationName')]",
|
||||
"type": "Microsoft.Insights/dataCollectionRuleAssociations",
|
||||
"apiVersion": "2021-04-01",
|
||||
"properties": {
|
||||
"dataCollectionRuleId": "[parameters('dcrResourceId')]"
|
||||
},
|
||||
"scope": "[concat('Microsoft.Compute/virtualMachineScaleSets/', parameters('resourceName'))]"
|
||||
},
|
||||
{
|
||||
"condition": "[equals(parameters('resourceType'), variables('dceResourceType'))]",
|
||||
"name": "[variables('dceAssociationName')]",
|
||||
"type": "Microsoft.Insights/dataCollectionRuleAssociations",
|
||||
"apiVersion": "2021-04-01",
|
||||
"properties": {
|
||||
"dataCollectionEndpointId": "[parameters('dcrResourceId')]"
|
||||
},
|
||||
"scope": "[concat('Microsoft.Compute/virtualMachineScaleSets/', parameters('resourceName'))]"
|
||||
}
|
||||
]
|
||||
},
|
||||
"parameters": {
|
||||
"resourceName": {
|
||||
"value": "[field('name')]"
|
||||
},
|
||||
"location": {
|
||||
"value": "[field('location')]"
|
||||
},
|
||||
"dcrResourceId": {
|
||||
"value": "[parameters('dcrResourceId')]"
|
||||
},
|
||||
"resourceType": {
|
||||
"value": "[parameters('resourceType')]"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cases:
|
||||
# =========================================================================
|
||||
# DINE - Linux VMSS with Canonical/UbuntuServer image, no DCRA
|
||||
# =========================================================================
|
||||
|
||||
- note: dine_vmss_canonical_ubuntu
|
||||
resource:
|
||||
type: "Microsoft.Compute/virtualMachineScaleSets"
|
||||
name: "vmss-ubuntu"
|
||||
id: "/subscriptions/sub1/resourceGroups/rg1/providers/Microsoft.Compute/virtualMachineScaleSets/vmss-ubuntu"
|
||||
location: "eastus"
|
||||
properties:
|
||||
virtualMachineProfile:
|
||||
storageProfile:
|
||||
osDisk:
|
||||
osType: "Linux"
|
||||
imageReference:
|
||||
publisher: "Canonical"
|
||||
offer: "UbuntuServer"
|
||||
sku: "18.04-lts"
|
||||
parameters:
|
||||
dcrResourceId: "/subscriptions/sub1/resourceGroups/rg1/providers/Microsoft.Insights/dataCollectionRules/dcr1"
|
||||
effect: "DeployIfNotExists"
|
||||
scopeToSupportedImages: true
|
||||
listOfLinuxImageIdToInclude: []
|
||||
resourceType: "Microsoft.Insights/dataCollectionRules"
|
||||
host_await:
|
||||
- key:
|
||||
operation: "lookup_related_resources"
|
||||
type: "Microsoft.Insights/dataCollectionRuleAssociations"
|
||||
response: null
|
||||
want_effect: "DeployIfNotExists"
|
||||
|
||||
# =========================================================================
|
||||
# Pass - Windows VMSS (wrong OS type)
|
||||
# =========================================================================
|
||||
|
||||
- note: pass_wrong_os_type
|
||||
resource:
|
||||
type: "Microsoft.Compute/virtualMachineScaleSets"
|
||||
name: "vmss-windows"
|
||||
location: "eastus"
|
||||
properties:
|
||||
virtualMachineProfile:
|
||||
storageProfile:
|
||||
osDisk:
|
||||
osType: "Windows"
|
||||
imageReference:
|
||||
publisher: "MicrosoftWindowsServer"
|
||||
offer: "WindowsServer"
|
||||
sku: "2019-Datacenter"
|
||||
parameters:
|
||||
dcrResourceId: "/subscriptions/sub1/resourceGroups/rg1/providers/Microsoft.Insights/dataCollectionRules/dcr1"
|
||||
effect: "DeployIfNotExists"
|
||||
scopeToSupportedImages: true
|
||||
listOfLinuxImageIdToInclude: []
|
||||
resourceType: "Microsoft.Insights/dataCollectionRules"
|
||||
want_undefined: true
|
||||
|
||||
# =========================================================================
|
||||
# DINE - VMSS with unsupported publisher but scopeToSupportedImages=false
|
||||
# =========================================================================
|
||||
|
||||
- note: dine_vmss_scope_bypass
|
||||
resource:
|
||||
type: "Microsoft.Compute/virtualMachineScaleSets"
|
||||
name: "vmss-custom"
|
||||
id: "/subscriptions/sub1/resourceGroups/rg1/providers/Microsoft.Compute/virtualMachineScaleSets/vmss-custom"
|
||||
location: "eastus"
|
||||
properties:
|
||||
virtualMachineProfile:
|
||||
storageProfile:
|
||||
osDisk:
|
||||
osType: "Linux"
|
||||
imageReference:
|
||||
publisher: "CustomPublisher"
|
||||
offer: "CustomLinux"
|
||||
sku: "1.0"
|
||||
parameters:
|
||||
dcrResourceId: "/subscriptions/sub1/resourceGroups/rg1/providers/Microsoft.Insights/dataCollectionRules/dcr1"
|
||||
effect: "DeployIfNotExists"
|
||||
scopeToSupportedImages: false
|
||||
listOfLinuxImageIdToInclude: []
|
||||
resourceType: "Microsoft.Insights/dataCollectionRules"
|
||||
host_await:
|
||||
- key:
|
||||
operation: "lookup_related_resources"
|
||||
type: "Microsoft.Insights/dataCollectionRuleAssociations"
|
||||
response: null
|
||||
want_effect: "DeployIfNotExists"
|
||||
|
||||
# =========================================================================
|
||||
# Pass - unsupported publisher with scopeToSupportedImages=true
|
||||
# =========================================================================
|
||||
|
||||
- note: pass_unsupported_publisher_scoped
|
||||
resource:
|
||||
type: "Microsoft.Compute/virtualMachineScaleSets"
|
||||
name: "vmss-custom-scoped"
|
||||
location: "eastus"
|
||||
properties:
|
||||
virtualMachineProfile:
|
||||
storageProfile:
|
||||
osDisk:
|
||||
osType: "Linux"
|
||||
imageReference:
|
||||
publisher: "CustomPublisher"
|
||||
offer: "CustomLinux"
|
||||
sku: "1.0"
|
||||
parameters:
|
||||
dcrResourceId: "/subscriptions/sub1/resourceGroups/rg1/providers/Microsoft.Insights/dataCollectionRules/dcr1"
|
||||
effect: "DeployIfNotExists"
|
||||
scopeToSupportedImages: true
|
||||
listOfLinuxImageIdToInclude: []
|
||||
resourceType: "Microsoft.Insights/dataCollectionRules"
|
||||
want_undefined: true
|
||||
|
||||
# =========================================================================
|
||||
# Skip - wrong resource type
|
||||
# =========================================================================
|
||||
|
||||
- note: skip_wrong_type
|
||||
resource:
|
||||
type: "Microsoft.Compute/virtualMachines"
|
||||
name: "vm-linux"
|
||||
location: "eastus"
|
||||
properties:
|
||||
storageProfile:
|
||||
osDisk:
|
||||
osType: "Linux"
|
||||
imageReference:
|
||||
publisher: "Canonical"
|
||||
offer: "UbuntuServer"
|
||||
sku: "18.04-lts"
|
||||
parameters:
|
||||
dcrResourceId: "/subscriptions/sub1/resourceGroups/rg1/providers/Microsoft.Insights/dataCollectionRules/dcr1"
|
||||
effect: "DeployIfNotExists"
|
||||
scopeToSupportedImages: true
|
||||
listOfLinuxImageIdToInclude: []
|
||||
resourceType: "Microsoft.Insights/dataCollectionRules"
|
||||
want_undefined: true
|
||||
130
tests/azure_policy/cases/e2e_double_encryption.yaml
Normal file
130
tests/azure_policy/cases/e2e_double_encryption.yaml
Normal file
@@ -0,0 +1,130 @@
|
||||
# Copyright (c) Microsoft Corporation.
|
||||
# Licensed under the MIT License.
|
||||
|
||||
# E2E Test: Compute/DoubleEncryptionRequired_Deny
|
||||
# Real Azure Policy: "Managed disks should be double encrypted with both
|
||||
# platform-managed and customer-managed keys"
|
||||
# Features: allOf, field (type + alias), equals, notEquals, parameters() with
|
||||
# defaultValue and allowedValues, parameterized effect
|
||||
|
||||
aliases: test_aliases.json
|
||||
|
||||
policy_definition: |
|
||||
{
|
||||
"properties": {
|
||||
"displayName": "Managed disks should be double encrypted",
|
||||
"policyType": "BuiltIn",
|
||||
"mode": "Indexed",
|
||||
"parameters": {
|
||||
"effect": {
|
||||
"type": "string",
|
||||
"defaultValue": "Audit",
|
||||
"allowedValues": ["Audit", "Deny", "Disabled"],
|
||||
"metadata": {
|
||||
"displayName": "Effect",
|
||||
"description": "Enable or disable the execution of the policy"
|
||||
}
|
||||
}
|
||||
},
|
||||
"policyRule": {
|
||||
"if": {
|
||||
"allOf": [
|
||||
{
|
||||
"field": "type",
|
||||
"equals": "Microsoft.Compute/diskEncryptionSets"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.Compute/diskEncryptionSets/encryptionType",
|
||||
"notEquals": "EncryptionAtRestWithPlatformAndCustomerKeys"
|
||||
}
|
||||
]
|
||||
},
|
||||
"then": {
|
||||
"effect": "[parameters('effect')]"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cases:
|
||||
# =========================================================================
|
||||
# Audit (default effect) — wrong encryption type
|
||||
# =========================================================================
|
||||
|
||||
- note: audit_single_key_encryption
|
||||
resource:
|
||||
type: "Microsoft.Compute/diskEncryptionSets"
|
||||
name: "myDES"
|
||||
location: "eastus"
|
||||
properties:
|
||||
encryptionType: "EncryptionAtRestWithCustomerKey"
|
||||
want_effect: "Audit"
|
||||
|
||||
# =========================================================================
|
||||
# No effect — correct double encryption
|
||||
# =========================================================================
|
||||
|
||||
- note: pass_double_encryption
|
||||
resource:
|
||||
type: "Microsoft.Compute/diskEncryptionSets"
|
||||
name: "myDES"
|
||||
location: "eastus"
|
||||
properties:
|
||||
encryptionType: "EncryptionAtRestWithPlatformAndCustomerKeys"
|
||||
want_undefined: true
|
||||
|
||||
# =========================================================================
|
||||
# No effect — wrong resource type
|
||||
# =========================================================================
|
||||
|
||||
- note: skip_wrong_type
|
||||
resource:
|
||||
type: "Microsoft.Compute/virtualMachines"
|
||||
name: "myVM"
|
||||
location: "eastus"
|
||||
properties:
|
||||
hardwareProfile:
|
||||
vmSize: "Standard_D2s_v3"
|
||||
want_undefined: true
|
||||
|
||||
# =========================================================================
|
||||
# Deny — explicit effect parameter override
|
||||
# =========================================================================
|
||||
|
||||
- note: deny_with_explicit_effect
|
||||
resource:
|
||||
type: "Microsoft.Compute/diskEncryptionSets"
|
||||
name: "myDES"
|
||||
location: "westus"
|
||||
properties:
|
||||
encryptionType: "EncryptionAtRestWithCustomerKey"
|
||||
parameters:
|
||||
effect: "Deny"
|
||||
want_effect: "Deny"
|
||||
|
||||
# =========================================================================
|
||||
# Audit — platform-only encryption (not double)
|
||||
# =========================================================================
|
||||
|
||||
- note: audit_platform_only_encryption
|
||||
resource:
|
||||
type: "Microsoft.Compute/diskEncryptionSets"
|
||||
name: "platformDES"
|
||||
location: "eastus"
|
||||
properties:
|
||||
encryptionType: "EncryptionAtRestWithPlatformKey"
|
||||
want_effect: "Audit"
|
||||
|
||||
# =========================================================================
|
||||
# No effect — encryption type missing (field is undefined/null)
|
||||
# notEquals with null LHS: Azure Policy treats missing field as null,
|
||||
# and null notEquals "string" is true → should fire
|
||||
# =========================================================================
|
||||
|
||||
- note: audit_missing_encryption_type
|
||||
resource:
|
||||
type: "Microsoft.Compute/diskEncryptionSets"
|
||||
name: "noPropDES"
|
||||
location: "eastus"
|
||||
properties: {}
|
||||
want_effect: "Audit"
|
||||
247
tests/azure_policy/cases/e2e_fic_aks_issuer.yaml
Normal file
247
tests/azure_policy/cases/e2e_fic_aks_issuer.yaml
Normal file
@@ -0,0 +1,247 @@
|
||||
# Copyright (c) Microsoft Corporation.
|
||||
# Licensed under the MIT License.
|
||||
|
||||
# E2E Test: Managed Identity/FIC_LimitToAzureKubernetesIssuer
|
||||
# Real Azure Policy: "Managed Identity Federated Credentials from Azure
|
||||
# Kubernetes should be from trusted sources"
|
||||
# Source: regolator/policyDefinitions/Managed Identity/FIC_LimitToAzureKubernetesIssuer.json
|
||||
#
|
||||
# Features exercised:
|
||||
# - Value count (count over parameter arrays)
|
||||
# - Complex nested if/split/length value expressions to parse issuer URL
|
||||
# - Double negation: not { anyOf [...] }
|
||||
# - Child resource type (sub-resource)
|
||||
# - `like` operator with wildcard pattern
|
||||
|
||||
aliases: test_aliases.json
|
||||
|
||||
policy_definition: |
|
||||
{
|
||||
"properties": {
|
||||
"displayName": "[Preview]: Managed Identity Federated Credentials from Azure Kubernetes should be from trusted sources",
|
||||
"policyType": "BuiltIn",
|
||||
"mode": "All",
|
||||
"parameters": {
|
||||
"allowedTenants": {
|
||||
"type": "Array",
|
||||
"metadata": {
|
||||
"displayName": "Allowed tenants",
|
||||
"description": "The list of allowed Azure AD tenant ID's of AKS OIDC issuers. Empty to allow all tenants."
|
||||
}
|
||||
},
|
||||
"allowedLocations": {
|
||||
"type": "Array",
|
||||
"defaultValue": [],
|
||||
"metadata": {
|
||||
"displayName": "Allowed locations",
|
||||
"description": "The list of allowed locations for AKS OIDC issuers. Empty to allow any location."
|
||||
}
|
||||
},
|
||||
"allowedClusterExceptions": {
|
||||
"type": "Array",
|
||||
"defaultValue": [],
|
||||
"metadata": {
|
||||
"displayName": "Allowed Exception Clusters",
|
||||
"description": "The list of specific cluster ids that will be exceptions to the location and tenant rules."
|
||||
}
|
||||
},
|
||||
"effect": {
|
||||
"type": "String",
|
||||
"defaultValue": "Audit",
|
||||
"allowedValues": ["Audit", "Disabled", "Deny"]
|
||||
}
|
||||
},
|
||||
"policyRule": {
|
||||
"if": {
|
||||
"allOf": [
|
||||
{
|
||||
"field": "type",
|
||||
"equals": "Microsoft.ManagedIdentity/userAssignedIdentities/federatedIdentityCredentials"
|
||||
},
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"value": "[if(greaterOrEquals(length(split(field('Microsoft.ManagedIdentity/userAssignedIdentities/federatedIdentityCredentials/issuer'),'/')),3),split(field('Microsoft.ManagedIdentity/userAssignedIdentities/federatedIdentityCredentials/issuer'),'/')[2],'')]",
|
||||
"like": "*.oic.prod-aks.azure.com"
|
||||
},
|
||||
{
|
||||
"not": {
|
||||
"anyOf": [
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"anyOf": [
|
||||
{
|
||||
"count": {
|
||||
"value": "[parameters('allowedLocations')]"
|
||||
},
|
||||
"equals": 0
|
||||
},
|
||||
{
|
||||
"value": "[if(greaterOrEquals(length(split(if(greaterOrEquals(length(split(field('Microsoft.ManagedIdentity/userAssignedIdentities/federatedIdentityCredentials/issuer'),'/')),3),split(field('Microsoft.ManagedIdentity/userAssignedIdentities/federatedIdentityCredentials/issuer'),'/')[2],''), '.')),1),split(if(greaterOrEquals(length(split(field('Microsoft.ManagedIdentity/userAssignedIdentities/federatedIdentityCredentials/issuer'),'/')),3),split(field('Microsoft.ManagedIdentity/userAssignedIdentities/federatedIdentityCredentials/issuer'),'/')[2],''), '.')[0],'')]",
|
||||
"in": "[parameters('allowedLocations')]"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"anyOf": [
|
||||
{
|
||||
"count": {
|
||||
"value": "[parameters('allowedTenants')]"
|
||||
},
|
||||
"equals": 0
|
||||
},
|
||||
{
|
||||
"value": "[if(greaterOrEquals(length(split(field('Microsoft.ManagedIdentity/userAssignedIdentities/federatedIdentityCredentials/issuer'),'/')),4),split(field('Microsoft.ManagedIdentity/userAssignedIdentities/federatedIdentityCredentials/issuer'),'/')[3],'')]",
|
||||
"in": "[parameters('allowedTenants')]"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"value": "[if(greaterOrEquals(length(split(field('Microsoft.ManagedIdentity/userAssignedIdentities/federatedIdentityCredentials/issuer'),'/')),5),split(field('Microsoft.ManagedIdentity/userAssignedIdentities/federatedIdentityCredentials/issuer'),'/')[4],'')]",
|
||||
"in": "[parameters('allowedClusterExceptions')]"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"then": {
|
||||
"effect": "[parameters('effect')]"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cases:
|
||||
# =========================================================================
|
||||
# Audit — untrusted AKS issuer (wrong tenant AND wrong location)
|
||||
# =========================================================================
|
||||
|
||||
- note: audit_untrusted_tenant_and_location
|
||||
resource:
|
||||
type: "Microsoft.ManagedIdentity/userAssignedIdentities/federatedIdentityCredentials"
|
||||
name: "aks-fic"
|
||||
properties:
|
||||
issuer: "https://eastus.oic.prod-aks.azure.com/bad-tenant-id/some-cluster-id"
|
||||
subject: "system:serviceaccount:default:workload-identity-sa"
|
||||
parameters:
|
||||
allowedTenants: ["good-tenant-id"]
|
||||
allowedLocations: ["westus"]
|
||||
allowedClusterExceptions: []
|
||||
want_effect: "Audit"
|
||||
|
||||
# =========================================================================
|
||||
# Audit — correct location but wrong tenant
|
||||
# =========================================================================
|
||||
|
||||
- note: audit_wrong_tenant_correct_location
|
||||
resource:
|
||||
type: "Microsoft.ManagedIdentity/userAssignedIdentities/federatedIdentityCredentials"
|
||||
name: "aks-fic"
|
||||
properties:
|
||||
issuer: "https://eastus.oic.prod-aks.azure.com/bad-tenant-id/some-cluster-id"
|
||||
subject: "system:serviceaccount:default:workload-identity-sa"
|
||||
parameters:
|
||||
allowedTenants: ["good-tenant-id"]
|
||||
allowedLocations: ["eastus"]
|
||||
allowedClusterExceptions: []
|
||||
want_effect: "Audit"
|
||||
|
||||
# =========================================================================
|
||||
# Pass — allowed tenant and allowed location
|
||||
# =========================================================================
|
||||
|
||||
- note: pass_allowed_tenant_and_location
|
||||
resource:
|
||||
type: "Microsoft.ManagedIdentity/userAssignedIdentities/federatedIdentityCredentials"
|
||||
name: "aks-fic"
|
||||
properties:
|
||||
issuer: "https://eastus.oic.prod-aks.azure.com/good-tenant-id/some-cluster-id"
|
||||
subject: "system:serviceaccount:default:workload-identity-sa"
|
||||
parameters:
|
||||
allowedTenants: ["good-tenant-id", "other-tenant-id"]
|
||||
allowedLocations: ["eastus", "westus"]
|
||||
allowedClusterExceptions: []
|
||||
want_undefined: true
|
||||
|
||||
# =========================================================================
|
||||
# Pass — allowed tenant, empty allowedLocations (any location allowed)
|
||||
# =========================================================================
|
||||
|
||||
- note: pass_allowed_tenant_any_location
|
||||
resource:
|
||||
type: "Microsoft.ManagedIdentity/userAssignedIdentities/federatedIdentityCredentials"
|
||||
name: "aks-fic"
|
||||
properties:
|
||||
issuer: "https://westeurope.oic.prod-aks.azure.com/good-tenant-id/some-cluster-id"
|
||||
subject: "system:serviceaccount:default:workload-identity-sa"
|
||||
parameters:
|
||||
allowedTenants: ["good-tenant-id"]
|
||||
allowedLocations: []
|
||||
allowedClusterExceptions: []
|
||||
want_undefined: true
|
||||
|
||||
# =========================================================================
|
||||
# Pass — empty allowedTenants and empty allowedLocations (allow all)
|
||||
# =========================================================================
|
||||
|
||||
- note: pass_empty_tenants_and_locations_allows_all
|
||||
resource:
|
||||
type: "Microsoft.ManagedIdentity/userAssignedIdentities/federatedIdentityCredentials"
|
||||
name: "aks-fic"
|
||||
properties:
|
||||
issuer: "https://australiaeast.oic.prod-aks.azure.com/any-tenant/any-cluster"
|
||||
subject: "system:serviceaccount:kube-system:my-sa"
|
||||
parameters:
|
||||
allowedTenants: []
|
||||
allowedLocations: []
|
||||
allowedClusterExceptions: []
|
||||
want_undefined: true
|
||||
|
||||
# =========================================================================
|
||||
# Pass — cluster ID is in the exceptions list (bypasses tenant/location)
|
||||
# =========================================================================
|
||||
|
||||
- note: pass_cluster_in_exceptions
|
||||
resource:
|
||||
type: "Microsoft.ManagedIdentity/userAssignedIdentities/federatedIdentityCredentials"
|
||||
name: "aks-fic"
|
||||
properties:
|
||||
issuer: "https://eastus.oic.prod-aks.azure.com/untrusted-tenant/special-cluster-id"
|
||||
subject: "system:serviceaccount:default:workload-identity-sa"
|
||||
parameters:
|
||||
allowedTenants: ["other-tenant"]
|
||||
allowedLocations: ["westus"]
|
||||
allowedClusterExceptions: ["special-cluster-id"]
|
||||
want_undefined: true
|
||||
|
||||
# =========================================================================
|
||||
# Pass — non-AKS issuer (GitHub Actions OIDC)
|
||||
# =========================================================================
|
||||
|
||||
- note: pass_non_aks_issuer
|
||||
resource:
|
||||
type: "Microsoft.ManagedIdentity/userAssignedIdentities/federatedIdentityCredentials"
|
||||
name: "github-fic"
|
||||
properties:
|
||||
issuer: "https://token.actions.githubusercontent.com"
|
||||
subject: "repo:myorg/myrepo:ref:refs/heads/main"
|
||||
parameters:
|
||||
allowedTenants: ["some-tenant"]
|
||||
want_undefined: true
|
||||
|
||||
# =========================================================================
|
||||
# Skip — wrong resource type entirely
|
||||
# =========================================================================
|
||||
|
||||
- note: skip_wrong_resource_type
|
||||
resource:
|
||||
type: "Microsoft.ManagedIdentity/userAssignedIdentities"
|
||||
name: "my-identity"
|
||||
properties: {}
|
||||
want_undefined: true
|
||||
184
tests/azure_policy/cases/e2e_fic_github_issuer.yaml
Normal file
184
tests/azure_policy/cases/e2e_fic_github_issuer.yaml
Normal file
@@ -0,0 +1,184 @@
|
||||
# Copyright (c) Microsoft Corporation.
|
||||
# Licensed under the MIT License.
|
||||
|
||||
# E2E Test: Managed Identity/FIC_LimitToGitHubIssuer
|
||||
# Real Azure Policy: "Managed Identity Federated Credentials from GitHub
|
||||
# should be from trusted repository owners"
|
||||
# Source: regolator/policyDefinitions/Managed Identity/FIC_LimitToGitHubIssuer.json
|
||||
#
|
||||
# Features exercised:
|
||||
# - Value count (count over parameter array)
|
||||
# - Complex nested if/split/length value expressions to parse subject field
|
||||
# - Double negation: not { anyOf [...] }
|
||||
# - Child resource type (sub-resource)
|
||||
|
||||
aliases: test_aliases.json
|
||||
|
||||
policy_definition: |
|
||||
{
|
||||
"properties": {
|
||||
"displayName": "[Preview]: Managed Identity Federated Credentials from GitHub should be from trusted repository owners",
|
||||
"policyType": "BuiltIn",
|
||||
"mode": "All",
|
||||
"parameters": {
|
||||
"allowedRepoOwners": {
|
||||
"type": "Array",
|
||||
"metadata": {
|
||||
"displayName": "Allowed Repo Owners"
|
||||
}
|
||||
},
|
||||
"allowedRepoExceptions": {
|
||||
"type": "Array",
|
||||
"defaultValue": [],
|
||||
"metadata": {
|
||||
"displayName": "Allowed Repo Exceptions"
|
||||
}
|
||||
},
|
||||
"effect": {
|
||||
"type": "String",
|
||||
"defaultValue": "Audit",
|
||||
"allowedValues": ["Audit", "Disabled", "Deny"]
|
||||
}
|
||||
},
|
||||
"policyRule": {
|
||||
"if": {
|
||||
"allOf": [
|
||||
{
|
||||
"field": "type",
|
||||
"equals": "Microsoft.ManagedIdentity/userAssignedIdentities/federatedIdentityCredentials"
|
||||
},
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"field": "Microsoft.ManagedIdentity/userAssignedIdentities/federatedIdentityCredentials/issuer",
|
||||
"equals": "https://token.actions.githubusercontent.com"
|
||||
},
|
||||
{
|
||||
"not": {
|
||||
"anyOf": [
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"anyOf": [
|
||||
{
|
||||
"count": {
|
||||
"value": "[parameters('allowedRepoOwners')]"
|
||||
},
|
||||
"equals": 0
|
||||
},
|
||||
{
|
||||
"value": "[if(greaterOrEquals(length(split(if(greaterOrEquals(length(split(field('Microsoft.ManagedIdentity/userAssignedIdentities/federatedIdentityCredentials/subject'),':')),2),split(field('Microsoft.ManagedIdentity/userAssignedIdentities/federatedIdentityCredentials/subject'),':')[1],''), '/')),2),split(if(greaterOrEquals(length(split(field('Microsoft.ManagedIdentity/userAssignedIdentities/federatedIdentityCredentials/subject'),':')),2),split(field('Microsoft.ManagedIdentity/userAssignedIdentities/federatedIdentityCredentials/subject'),':')[1],''), '/')[0],'')]",
|
||||
"in": "[parameters('allowedRepoOwners')]"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"value": "[if(greaterOrEquals(length(split(field('Microsoft.ManagedIdentity/userAssignedIdentities/federatedIdentityCredentials/subject'),':')),2),split(field('Microsoft.ManagedIdentity/userAssignedIdentities/federatedIdentityCredentials/subject'),':')[1],'')]",
|
||||
"in": "[parameters('allowedRepoExceptions')]"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"then": {
|
||||
"effect": "[parameters('effect')]"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cases:
|
||||
# =========================================================================
|
||||
# Audit — untrusted repo owner (not in allowedRepoOwners)
|
||||
# =========================================================================
|
||||
|
||||
- note: audit_untrusted_repo_owner
|
||||
resource:
|
||||
type: "Microsoft.ManagedIdentity/userAssignedIdentities/federatedIdentityCredentials"
|
||||
name: "github-fic"
|
||||
properties:
|
||||
issuer: "https://token.actions.githubusercontent.com"
|
||||
subject: "repo:evil-org/malicious-repo:ref:refs/heads/main"
|
||||
parameters:
|
||||
allowedRepoOwners: ["trusted-org", "another-org"]
|
||||
allowedRepoExceptions: []
|
||||
want_effect: "Audit"
|
||||
|
||||
# =========================================================================
|
||||
# Pass — repo owner is in the allowed list
|
||||
# =========================================================================
|
||||
|
||||
- note: pass_allowed_repo_owner
|
||||
resource:
|
||||
type: "Microsoft.ManagedIdentity/userAssignedIdentities/federatedIdentityCredentials"
|
||||
name: "github-fic"
|
||||
properties:
|
||||
issuer: "https://token.actions.githubusercontent.com"
|
||||
subject: "repo:trusted-org/my-repo:ref:refs/heads/main"
|
||||
parameters:
|
||||
allowedRepoOwners: ["trusted-org", "another-org"]
|
||||
allowedRepoExceptions: []
|
||||
want_undefined: true
|
||||
|
||||
# =========================================================================
|
||||
# Pass — repo is in exceptions list (even if owner not allowed)
|
||||
# =========================================================================
|
||||
|
||||
- note: pass_repo_in_exceptions
|
||||
resource:
|
||||
type: "Microsoft.ManagedIdentity/userAssignedIdentities/federatedIdentityCredentials"
|
||||
name: "github-fic"
|
||||
properties:
|
||||
issuer: "https://token.actions.githubusercontent.com"
|
||||
subject: "repo:random-org/special-repo:ref:refs/heads/main"
|
||||
parameters:
|
||||
allowedRepoOwners: ["trusted-org"]
|
||||
allowedRepoExceptions: ["random-org/special-repo"]
|
||||
want_undefined: true
|
||||
|
||||
# =========================================================================
|
||||
# Pass — empty allowedRepoOwners means allow all
|
||||
# =========================================================================
|
||||
|
||||
- note: pass_empty_owners_allows_all
|
||||
resource:
|
||||
type: "Microsoft.ManagedIdentity/userAssignedIdentities/federatedIdentityCredentials"
|
||||
name: "github-fic"
|
||||
properties:
|
||||
issuer: "https://token.actions.githubusercontent.com"
|
||||
subject: "repo:any-org/any-repo:ref:refs/heads/main"
|
||||
parameters:
|
||||
allowedRepoOwners: []
|
||||
allowedRepoExceptions: []
|
||||
want_undefined: true
|
||||
|
||||
# =========================================================================
|
||||
# Pass — not a GitHub issuer (different OIDC provider)
|
||||
# =========================================================================
|
||||
|
||||
- note: pass_non_github_issuer
|
||||
resource:
|
||||
type: "Microsoft.ManagedIdentity/userAssignedIdentities/federatedIdentityCredentials"
|
||||
name: "aks-fic"
|
||||
properties:
|
||||
issuer: "https://oidc.prod-aks.azure.com/00000000-0000-0000-0000-000000000000"
|
||||
subject: "system:serviceaccount:default:workload-identity-sa"
|
||||
parameters:
|
||||
allowedRepoOwners: ["trusted-org"]
|
||||
want_undefined: true
|
||||
|
||||
# =========================================================================
|
||||
# Skip — wrong resource type
|
||||
# =========================================================================
|
||||
|
||||
- note: skip_wrong_type
|
||||
resource:
|
||||
type: "Microsoft.ManagedIdentity/userAssignedIdentities"
|
||||
name: "my-identity"
|
||||
properties: {}
|
||||
want_undefined: true
|
||||
183
tests/azure_policy/cases/e2e_functionapp_https_modify.yaml
Normal file
183
tests/azure_policy/cases/e2e_functionapp_https_modify.yaml
Normal file
@@ -0,0 +1,183 @@
|
||||
# Copyright (c) Microsoft Corporation.
|
||||
# Licensed under the MIT License.
|
||||
|
||||
# E2E Test: App Service/FunctionApp_AuditHTTP_Modify
|
||||
# Real Azure Policy: "Configure Function apps to only be accessible over HTTPS"
|
||||
# Source: regolator/policyDefinitions/App Service/FunctionApp_AuditHTTP_Modify.json
|
||||
#
|
||||
# Features exercised:
|
||||
# - kind contains / notContains string operators
|
||||
# - exists "false" — field doesn't exist or is null
|
||||
# - Modify with greaterOrEquals(requestContext().apiVersion,...) condition
|
||||
# - conflictEffect: audit
|
||||
|
||||
aliases: test_aliases.json
|
||||
|
||||
policy_definition: |
|
||||
{
|
||||
"properties": {
|
||||
"displayName": "Configure Function apps to only be accessible over HTTPS",
|
||||
"policyType": "BuiltIn",
|
||||
"mode": "Indexed",
|
||||
"parameters": {
|
||||
"effect": {
|
||||
"type": "string",
|
||||
"defaultValue": "Modify",
|
||||
"allowedValues": ["Modify", "Disabled"]
|
||||
}
|
||||
},
|
||||
"policyRule": {
|
||||
"if": {
|
||||
"allOf": [
|
||||
{
|
||||
"field": "type",
|
||||
"equals": "Microsoft.Web/sites"
|
||||
},
|
||||
{
|
||||
"field": "kind",
|
||||
"contains": "functionapp"
|
||||
},
|
||||
{
|
||||
"field": "kind",
|
||||
"notContains": "workflowapp"
|
||||
},
|
||||
{
|
||||
"anyOf": [
|
||||
{
|
||||
"field": "Microsoft.Web/sites/httpsOnly",
|
||||
"exists": "false"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.Web/sites/httpsOnly",
|
||||
"equals": "false"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"then": {
|
||||
"effect": "[parameters('effect')]",
|
||||
"details": {
|
||||
"roleDefinitionIds": [
|
||||
"/providers/microsoft.authorization/roleDefinitions/de139f84-1756-47ae-9be6-808fbbe84772"
|
||||
],
|
||||
"conflictEffect": "audit",
|
||||
"operations": [
|
||||
{
|
||||
"condition": "[greaterOrEquals(requestContext().apiVersion, '2019-08-01')]",
|
||||
"operation": "addOrReplace",
|
||||
"field": "Microsoft.Web/sites/httpsOnly",
|
||||
"value": true
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cases:
|
||||
# =========================================================================
|
||||
# Function app with httpsOnly missing → Modify
|
||||
# =========================================================================
|
||||
|
||||
- note: modify_functionapp_httpsonly_missing
|
||||
resource:
|
||||
type: "Microsoft.Web/sites"
|
||||
kind: "functionapp"
|
||||
name: "func-no-https"
|
||||
properties: {}
|
||||
api_version: "2022-03-01"
|
||||
want_effect: "Modify"
|
||||
want_details:
|
||||
roleDefinitionIds:
|
||||
- "/providers/microsoft.authorization/roleDefinitions/de139f84-1756-47ae-9be6-808fbbe84772"
|
||||
operations:
|
||||
- condition: "[greaterOrEquals(requestContext().apiVersion, '2019-08-01')]"
|
||||
operation: "addOrReplace"
|
||||
field: "Microsoft.Web/sites/httpsOnly"
|
||||
value: true
|
||||
|
||||
# =========================================================================
|
||||
# Function app with httpsOnly = false → Modify
|
||||
# =========================================================================
|
||||
|
||||
- note: modify_functionapp_httpsonly_false
|
||||
resource:
|
||||
type: "Microsoft.Web/sites"
|
||||
kind: "functionapp,linux"
|
||||
name: "func-linux-no-https"
|
||||
properties:
|
||||
httpsOnly: false
|
||||
api_version: "2020-06-01"
|
||||
want_effect: "Modify"
|
||||
want_details:
|
||||
roleDefinitionIds:
|
||||
- "/providers/microsoft.authorization/roleDefinitions/de139f84-1756-47ae-9be6-808fbbe84772"
|
||||
operations:
|
||||
- condition: "[greaterOrEquals(requestContext().apiVersion, '2019-08-01')]"
|
||||
operation: "addOrReplace"
|
||||
field: "Microsoft.Web/sites/httpsOnly"
|
||||
value: true
|
||||
|
||||
# =========================================================================
|
||||
# Function app with httpsOnly = true → pass (condition not met)
|
||||
# =========================================================================
|
||||
|
||||
- note: pass_functionapp_httpsonly_true
|
||||
resource:
|
||||
type: "Microsoft.Web/sites"
|
||||
kind: "functionapp"
|
||||
name: "func-https"
|
||||
properties:
|
||||
httpsOnly: true
|
||||
api_version: "2022-03-01"
|
||||
want_undefined: true
|
||||
|
||||
# =========================================================================
|
||||
# Workflow app (Logic App) — notContains "workflowapp" fails → pass
|
||||
# =========================================================================
|
||||
|
||||
- note: pass_workflowapp_excluded
|
||||
resource:
|
||||
type: "Microsoft.Web/sites"
|
||||
kind: "functionapp,workflowapp"
|
||||
name: "logic-app"
|
||||
properties: {}
|
||||
api_version: "2022-03-01"
|
||||
want_undefined: true
|
||||
|
||||
# =========================================================================
|
||||
# Web app (not function app) — contains "functionapp" fails → pass
|
||||
# =========================================================================
|
||||
|
||||
- note: pass_webapp_not_functionapp
|
||||
resource:
|
||||
type: "Microsoft.Web/sites"
|
||||
kind: "app"
|
||||
name: "web-app"
|
||||
properties:
|
||||
httpsOnly: false
|
||||
api_version: "2022-03-01"
|
||||
want_undefined: true
|
||||
|
||||
# =========================================================================
|
||||
# Old API version → operation condition not met, no operations emitted
|
||||
# =========================================================================
|
||||
|
||||
- note: modify_old_api_no_operations
|
||||
resource:
|
||||
type: "Microsoft.Web/sites"
|
||||
kind: "functionapp"
|
||||
name: "func-old-api"
|
||||
properties: {}
|
||||
api_version: "2018-02-01"
|
||||
want_effect: "Modify"
|
||||
want_details:
|
||||
roleDefinitionIds:
|
||||
- "/providers/microsoft.authorization/roleDefinitions/de139f84-1756-47ae-9be6-808fbbe84772"
|
||||
operations:
|
||||
- condition: "[greaterOrEquals(requestContext().apiVersion, '2019-08-01')]"
|
||||
operation: "addOrReplace"
|
||||
field: "Microsoft.Web/sites/httpsOnly"
|
||||
value: true
|
||||
836
tests/azure_policy/cases/e2e_guest_config_user_identity.yaml
Normal file
836
tests/azure_policy/cases/e2e_guest_config_user_identity.yaml
Normal file
@@ -0,0 +1,836 @@
|
||||
# Copyright (c) Microsoft Corporation.
|
||||
# Licensed under the MIT License.
|
||||
|
||||
# E2E Test: Guest Configuration/AddUserIdentity_Prerequisite
|
||||
# Real Azure Policy: "[Preview]: Add user-assigned managed identity to enable Guest Configuration assignments on virtual machines"
|
||||
# Source: regolator/policyDefinitions/Guest Configuration/AddUserIdentity_Prerequisite.json
|
||||
#
|
||||
# Features exercised:
|
||||
# - Self-referential DeployIfNotExists (details.type matches resource type)
|
||||
# - identity.type / identity.userAssignedIdentities existenceCondition
|
||||
# - containsKey with concat(subscription().subscriptionId, field('location'))
|
||||
# - Deep allOf/anyOf nesting for OS image publisher matching (Windows + Linux)
|
||||
# - requestContext().apiVersion guard (>= 2018-10-01)
|
||||
# - deploymentScope: subscription
|
||||
# - subscription-level ARM deployment template
|
||||
|
||||
aliases: test_aliases.json
|
||||
|
||||
policy_definition: |
|
||||
{
|
||||
"properties": {
|
||||
"displayName": "[Preview]: Add user-assigned managed identity to enable Guest Configuration assignments on virtual machines",
|
||||
"mode": "Indexed",
|
||||
"policyType": "BuiltIn",
|
||||
"description": "This policy adds a user-assigned managed identity to virtual machines hosted in Azure that are supported by Guest Configuration. A user-assigned managed identity is a prerequisite for all Guest Configuration assignments and must be added to machines before using any Guest Configuration policy definitions. For more information on Guest Configuration, visit https://aka.ms/gcpol.",
|
||||
"metadata": {
|
||||
"category": "Guest Configuration",
|
||||
"version": "2.1.0-preview",
|
||||
"preview": true
|
||||
},
|
||||
"version": "2.1.0-preview",
|
||||
"parameters": {
|
||||
"effect": {
|
||||
"type": "String",
|
||||
"metadata": {
|
||||
"displayName": "Policy Effect",
|
||||
"description": "The effect determines what happens when the policy rule is evaluated to match."
|
||||
},
|
||||
"allowedValues": [
|
||||
"AuditIfNotExists",
|
||||
"DeployIfNotExists",
|
||||
"Disabled"
|
||||
],
|
||||
"defaultValue": "DeployIfNotExists"
|
||||
}
|
||||
},
|
||||
"policyRule": {
|
||||
"if": {
|
||||
"allOf": [
|
||||
{
|
||||
"field": "type",
|
||||
"equals": "Microsoft.Compute/virtualMachines"
|
||||
},
|
||||
{
|
||||
"anyOf": [
|
||||
{
|
||||
"anyOf": [
|
||||
{
|
||||
"field": "Microsoft.Compute/imagePublisher",
|
||||
"in": [
|
||||
"esri",
|
||||
"incredibuild",
|
||||
"MicrosoftDynamicsAX",
|
||||
"MicrosoftSharepoint",
|
||||
"MicrosoftVisualStudio",
|
||||
"MicrosoftWindowsDesktop",
|
||||
"MicrosoftWindowsServerHPCPack"
|
||||
]
|
||||
},
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"field": "Microsoft.Compute/imagePublisher",
|
||||
"equals": "MicrosoftWindowsServer"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.Compute/imageSKU",
|
||||
"notLike": "2008*"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"field": "Microsoft.Compute/imagePublisher",
|
||||
"equals": "MicrosoftSQLServer"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.Compute/imageOffer",
|
||||
"notLike": "SQL2008*"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"field": "Microsoft.Compute/imagePublisher",
|
||||
"equals": "microsoft-dsvm"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.Compute/imageOffer",
|
||||
"like": "dsvm-win*"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"field": "Microsoft.Compute/imagePublisher",
|
||||
"equals": "microsoft-ads"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.Compute/imageOffer",
|
||||
"in": [
|
||||
"standard-data-science-vm",
|
||||
"windows-data-science-vm"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"field": "Microsoft.Compute/imagePublisher",
|
||||
"equals": "batch"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.Compute/imageOffer",
|
||||
"equals": "rendering-windows2016"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"field": "Microsoft.Compute/imagePublisher",
|
||||
"equals": "center-for-internet-security-inc"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.Compute/imageOffer",
|
||||
"like": "cis-windows-server-201*"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"field": "Microsoft.Compute/imagePublisher",
|
||||
"equals": "pivotal"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.Compute/imageOffer",
|
||||
"like": "bosh-windows-server*"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"field": "Microsoft.Compute/imagePublisher",
|
||||
"equals": "cloud-infrastructure-services"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.Compute/imageOffer",
|
||||
"like": "ad*"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"anyOf": [
|
||||
{
|
||||
"field": "Microsoft.Compute/virtualMachines/osProfile.windowsConfiguration",
|
||||
"exists": "true"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.Compute/virtualMachines/storageProfile.osDisk.osType",
|
||||
"like": "Windows*"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"anyOf": [
|
||||
{
|
||||
"field": "Microsoft.Compute/imageSKU",
|
||||
"exists": "false"
|
||||
},
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"field": "Microsoft.Compute/imageSKU",
|
||||
"notLike": "2008*"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.Compute/imageOffer",
|
||||
"notLike": "SQL2008*"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"anyOf": [
|
||||
{
|
||||
"field": "Microsoft.Compute/imagePublisher",
|
||||
"in": [
|
||||
"microsoft-aks",
|
||||
"qubole-inc",
|
||||
"datastax",
|
||||
"couchbase",
|
||||
"scalegrid",
|
||||
"checkpoint",
|
||||
"paloaltonetworks",
|
||||
"debian",
|
||||
"credativ"
|
||||
]
|
||||
},
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"field": "Microsoft.Compute/imagePublisher",
|
||||
"equals": "OpenLogic"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.Compute/imageSKU",
|
||||
"notLike": "6*"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"field": "Microsoft.Compute/imagePublisher",
|
||||
"equals": "Oracle"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.Compute/imageSKU",
|
||||
"notLike": "6*"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"field": "Microsoft.Compute/imagePublisher",
|
||||
"equals": "RedHat"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.Compute/imageSKU",
|
||||
"notLike": "6*"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"field": "Microsoft.Compute/imagePublisher",
|
||||
"equals": "center-for-internet-security-inc"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.Compute/imageOffer",
|
||||
"notLike": "cis-win*"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"field": "Microsoft.Compute/imagePublisher",
|
||||
"equals": "Suse"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.Compute/imageSKU",
|
||||
"notLike": "11*"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"field": "Microsoft.Compute/imagePublisher",
|
||||
"equals": "Canonical"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.Compute/imageSKU",
|
||||
"notLike": "12*"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"field": "Microsoft.Compute/imagePublisher",
|
||||
"equals": "microsoft-dsvm"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.Compute/imageOffer",
|
||||
"notLike": "dsvm-win*"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"field": "Microsoft.Compute/imagePublisher",
|
||||
"equals": "cloudera"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.Compute/imageSKU",
|
||||
"notLike": "6*"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"field": "Microsoft.Compute/imagePublisher",
|
||||
"equals": "microsoft-ads"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.Compute/imageOffer",
|
||||
"like": "linux*"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"anyOf": [
|
||||
{
|
||||
"field": "Microsoft.Compute/virtualMachines/osProfile.linuxConfiguration",
|
||||
"exists": "true"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.Compute/virtualMachines/storageProfile.osDisk.osType",
|
||||
"like": "Linux*"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"anyOf": [
|
||||
{
|
||||
"field": "Microsoft.Compute/imagePublisher",
|
||||
"exists": "false"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.Compute/imagePublisher",
|
||||
"notIn": [
|
||||
"OpenLogic",
|
||||
"RedHat",
|
||||
"credativ",
|
||||
"Suse",
|
||||
"Canonical",
|
||||
"microsoft-dsvm",
|
||||
"cloudera",
|
||||
"microsoft-ads",
|
||||
"center-for-internet-security-inc",
|
||||
"Oracle",
|
||||
"AzureDatabricks",
|
||||
"azureopenshift"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"value": "[requestContext().apiVersion]",
|
||||
"greaterOrEquals": "2018-10-01"
|
||||
}
|
||||
]
|
||||
},
|
||||
"then": {
|
||||
"effect": "[parameters('effect')]",
|
||||
"details": {
|
||||
"type": "Microsoft.Compute/virtualMachines",
|
||||
"name": "[field('name')]",
|
||||
"evaluationDelay": "AfterProvisioning",
|
||||
"deploymentScope": "subscription",
|
||||
"existenceCondition": {
|
||||
"allOf": [
|
||||
{
|
||||
"field": "identity.type",
|
||||
"contains": "UserAssigned"
|
||||
},
|
||||
{
|
||||
"field": "identity.userAssignedIdentities",
|
||||
"containsKey": "[concat('/subscriptions/', subscription().subscriptionId, '/resourceGroups/Built-In-Identity-RG/providers/Microsoft.ManagedIdentity/userAssignedIdentities/Built-In-Identity-', field('location'))]"
|
||||
}
|
||||
]
|
||||
},
|
||||
"roleDefinitionIds": [
|
||||
"/providers/Microsoft.Authorization/roleDefinitions/b24988ac-6180-42a0-ab88-20f7382dd24c",
|
||||
"/providers/Microsoft.Authorization/roleDefinitions/18d7d88d-d35e-4fb5-a5c3-7773c20a72d9"
|
||||
],
|
||||
"deployment": {
|
||||
"location": "eastus",
|
||||
"properties": {
|
||||
"mode": "incremental",
|
||||
"parameters": {
|
||||
"bringYourOwnUserAssignedManagedIdentity": {
|
||||
"value": false
|
||||
},
|
||||
"location": {
|
||||
"value": "[field('location')]"
|
||||
},
|
||||
"uaName": {
|
||||
"value": "Built-In-Identity"
|
||||
},
|
||||
"identityResourceGroup": {
|
||||
"value": "Built-In-Identity-RG"
|
||||
},
|
||||
"vmName": {
|
||||
"value": "[field('name')]"
|
||||
},
|
||||
"vmResourceGroup": {
|
||||
"value": "[resourceGroup().name]"
|
||||
},
|
||||
"resourceId": {
|
||||
"value": "[field('id')]"
|
||||
}
|
||||
},
|
||||
"template": {
|
||||
"$schema": "https://schema.management.azure.com/schemas/2018-05-01/subscriptionDeploymentTemplate.json#",
|
||||
"contentVersion": "1.0.0.1",
|
||||
"parameters": {
|
||||
"bringYourOwnUserAssignedManagedIdentity": {
|
||||
"type": "bool"
|
||||
},
|
||||
"location": {
|
||||
"type": "string"
|
||||
},
|
||||
"uaName": {
|
||||
"type": "string"
|
||||
},
|
||||
"identityResourceGroup": {
|
||||
"type": "string"
|
||||
},
|
||||
"vmName": {
|
||||
"type": "string"
|
||||
},
|
||||
"vmResourceGroup": {
|
||||
"type": "string"
|
||||
},
|
||||
"resourceId": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"variables": {
|
||||
"uaNameWithLocation": "[concat(parameters('uaName'),'-', parameters('location'))]",
|
||||
"precreatedUaId": "[concat('/subscriptions/', subscription().subscriptionId, '/resourceGroups/', trim(parameters('identityResourceGroup')), '/providers/Microsoft.ManagedIdentity/userAssignedIdentities/', trim(parameters('uaName')))]",
|
||||
"autocreatedUaId": "[concat('/subscriptions/', subscription().subscriptionId, '/resourceGroups/', trim(parameters('identityResourceGroup')), '/providers/Microsoft.ManagedIdentity/userAssignedIdentities/', trim(parameters('uaName')), '-', parameters('location'))]",
|
||||
"deployUALockName": "[concat('deployUALock-', uniqueString(deployment().name))]",
|
||||
"deployUAName": "[concat('deployUA-', uniqueString(deployment().name))]",
|
||||
"deployGetResourceProperties": "[concat('deployGetResourceProperties-', uniqueString(deployment().name))]",
|
||||
"deployAssignUAName": "[concat('deployAssignUA-', uniqueString(deployment().name))]"
|
||||
},
|
||||
"resources": [
|
||||
{
|
||||
"condition": "[not(parameters('bringYourOwnUserAssignedManagedIdentity'))]",
|
||||
"type": "Microsoft.Resources/resourceGroups",
|
||||
"apiVersion": "2020-06-01",
|
||||
"name": "[parameters('identityResourceGroup')]",
|
||||
"location": "eastus"
|
||||
},
|
||||
{
|
||||
"condition": "[parameters('bringYourOwnUserAssignedManagedIdentity')]",
|
||||
"type": "Microsoft.Resources/deployments",
|
||||
"apiVersion": "2020-06-01",
|
||||
"name": "[variables('deployUALockName')]",
|
||||
"resourceGroup": "[parameters('identityResourceGroup')]",
|
||||
"properties": {
|
||||
"mode": "Incremental",
|
||||
"expressionEvaluationOptions": {
|
||||
"scope": "inner"
|
||||
},
|
||||
"parameters": {
|
||||
"uaName": {
|
||||
"value": "[parameters('uaName')]"
|
||||
}
|
||||
},
|
||||
"template": {
|
||||
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
|
||||
"contentVersion": "1.0.0.0",
|
||||
"parameters": {
|
||||
"uaName": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"variables": {},
|
||||
"resources": [
|
||||
{
|
||||
"type": "Microsoft.Authorization/locks",
|
||||
"apiVersion": "2016-09-01",
|
||||
"name": "[concat('CanNotDeleteLock-', parameters('uaName'))]",
|
||||
"scope": "[concat('Microsoft.ManagedIdentity/userAssignedIdentities/', parameters('uaName'))]",
|
||||
"properties": {
|
||||
"level": "CanNotDelete",
|
||||
"notes": "Please do not delete this User-Assigned Identity since extensions enabled by Azure Policy are relying on their existence."
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"condition": "[not(parameters('bringYourOwnUserAssignedManagedIdentity'))]",
|
||||
"type": "Microsoft.Resources/deployments",
|
||||
"apiVersion": "2020-06-01",
|
||||
"name": "[variables('deployUAName')]",
|
||||
"resourceGroup": "[parameters('identityResourceGroup')]",
|
||||
"dependsOn": [
|
||||
"[resourceId('Microsoft.Resources/resourceGroups', parameters('identityResourceGroup'))]"
|
||||
],
|
||||
"properties": {
|
||||
"mode": "Incremental",
|
||||
"expressionEvaluationOptions": {
|
||||
"scope": "inner"
|
||||
},
|
||||
"parameters": {
|
||||
"uaName": {
|
||||
"value": "[variables('uaNameWithLocation')]"
|
||||
},
|
||||
"location": {
|
||||
"value": "[parameters('location')]"
|
||||
}
|
||||
},
|
||||
"template": {
|
||||
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
|
||||
"contentVersion": "1.0.0.0",
|
||||
"parameters": {
|
||||
"uaName": {
|
||||
"type": "string"
|
||||
},
|
||||
"location": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"variables": {},
|
||||
"resources": [
|
||||
{
|
||||
"type": "Microsoft.ManagedIdentity/userAssignedIdentities",
|
||||
"name": "[parameters('uaName')]",
|
||||
"apiVersion": "2018-11-30",
|
||||
"location": "[parameters('location')]"
|
||||
},
|
||||
{
|
||||
"type": "Microsoft.ManagedIdentity/userAssignedIdentities/providers/locks",
|
||||
"apiVersion": "2016-09-01",
|
||||
"name": "[concat(parameters('uaName'), '/Microsoft.Authorization/', 'CanNotDeleteLock-', parameters('uaName'))]",
|
||||
"dependsOn": [
|
||||
"[parameters('uaName')]"
|
||||
],
|
||||
"properties": {
|
||||
"level": "CanNotDelete",
|
||||
"notes": "Please do not delete this User-Assigned Identity since extensions enabled by Azure Policy are relying on their existence."
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "Microsoft.Resources/deployments",
|
||||
"apiVersion": "2020-06-01",
|
||||
"name": "[variables('deployGetResourceProperties')]",
|
||||
"location": "eastus",
|
||||
"dependsOn": [
|
||||
"[resourceId('Microsoft.Resources/resourceGroups', parameters('identityResourceGroup'))]",
|
||||
"[variables('deployUAName')]"
|
||||
],
|
||||
"properties": {
|
||||
"mode": "Incremental",
|
||||
"template": {
|
||||
"$schema": "http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
|
||||
"contentVersion": "1.0.0.0",
|
||||
"resources": [],
|
||||
"outputs": {
|
||||
"resource": {
|
||||
"type": "object",
|
||||
"value": "[reference(parameters('resourceId'), '2019-07-01', 'Full')]"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "Microsoft.Resources/deployments",
|
||||
"apiVersion": "2020-06-01",
|
||||
"name": "[concat(variables('deployAssignUAName'))]",
|
||||
"resourceGroup": "[parameters('vmResourceGroup')]",
|
||||
"dependsOn": [
|
||||
"[resourceId('Microsoft.Resources/resourceGroups', parameters('identityResourceGroup'))]",
|
||||
"[variables('deployUAName')]",
|
||||
"[variables('deployGetResourceProperties')]"
|
||||
],
|
||||
"properties": {
|
||||
"mode": "Incremental",
|
||||
"expressionEvaluationOptions": {
|
||||
"scope": "inner"
|
||||
},
|
||||
"parameters": {
|
||||
"uaId": {
|
||||
"value": "[if(parameters('bringYourOwnUserAssignedManagedIdentity'), variables('precreatedUaId'), variables('autocreatedUaId'))]"
|
||||
},
|
||||
"vmName": {
|
||||
"value": "[parameters('vmName')]"
|
||||
},
|
||||
"location": {
|
||||
"value": "[parameters('location')]"
|
||||
},
|
||||
"identityType": {
|
||||
"value": "[if(contains(reference(variables('deployGetResourceProperties')).outputs.resource.value, 'identity'), reference(variables('deployGetResourceProperties')).outputs.resource.value.identity.type, '')]"
|
||||
},
|
||||
"userAssignedIdentities": {
|
||||
"value": "[if(and(contains(reference(variables('deployGetResourceProperties')).outputs.resource.value, 'identity'), contains(reference(variables('deployGetResourceProperties')).outputs.resource.value.identity, 'userAssignedIdentities')), reference(variables('deployGetResourceProperties')).outputs.resource.value.identity.userAssignedIdentities, createObject())]"
|
||||
}
|
||||
},
|
||||
"template": {
|
||||
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
|
||||
"contentVersion": "1.0.0.0",
|
||||
"parameters": {
|
||||
"uaId": {
|
||||
"type": "string"
|
||||
},
|
||||
"vmName": {
|
||||
"type": "string"
|
||||
},
|
||||
"location": {
|
||||
"type": "string"
|
||||
},
|
||||
"identityType": {
|
||||
"type": "string"
|
||||
},
|
||||
"userAssignedIdentities": {
|
||||
"type": "object"
|
||||
}
|
||||
},
|
||||
"variables": {
|
||||
"identityTypeValue": "[if(contains(parameters('identityType'), 'SystemAssigned'), 'SystemAssigned,UserAssigned', 'UserAssigned')]",
|
||||
"userAssignedIdentitiesValue": "[union(parameters('userAssignedIdentities'), createObject(parameters('uaId'), createObject()))]"
|
||||
},
|
||||
"resources": [
|
||||
{
|
||||
"apiVersion": "2019-07-01",
|
||||
"type": "Microsoft.Compute/virtualMachines",
|
||||
"name": "[parameters('vmName')]",
|
||||
"location": "[parameters('location')]",
|
||||
"identity": {
|
||||
"type": "[variables('identityTypeValue')]",
|
||||
"userAssignedIdentities": "[variables('userAssignedIdentitiesValue')]"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"versions": [
|
||||
"2.1.0-PREVIEW",
|
||||
"2.0.1-PREVIEW"
|
||||
]
|
||||
}
|
||||
|
||||
cases:
|
||||
# =========================================================================
|
||||
# DINE — Windows VM (MicrosoftWindowsServer), no user-assigned identity
|
||||
# → existenceCondition fails → DeployIfNotExists
|
||||
# =========================================================================
|
||||
|
||||
- note: dine_windows_vm_no_identity
|
||||
resource:
|
||||
type: "Microsoft.Compute/virtualMachines"
|
||||
name: "vm-win-noidentity"
|
||||
id: "/subscriptions/sub1/resourceGroups/rg1/providers/Microsoft.Compute/virtualMachines/vm-win-noidentity"
|
||||
location: "eastus"
|
||||
properties:
|
||||
storageProfile:
|
||||
imageReference:
|
||||
publisher: "MicrosoftWindowsServer"
|
||||
offer: "WindowsServer"
|
||||
sku: "2019-Datacenter"
|
||||
osDisk:
|
||||
osType: "Windows"
|
||||
osProfile:
|
||||
windowsConfiguration: {}
|
||||
request_context:
|
||||
apiVersion: "2024-01-01"
|
||||
host_await:
|
||||
- key:
|
||||
operation: "lookup_related_resources"
|
||||
type: "Microsoft.Compute/virtualMachines"
|
||||
response: null
|
||||
want_effect: "DeployIfNotExists"
|
||||
|
||||
# =========================================================================
|
||||
# Pass — Windows VM with proper user-assigned identity in existence result
|
||||
# → existenceCondition passes → compliant
|
||||
# =========================================================================
|
||||
|
||||
- note: pass_windows_vm_with_identity
|
||||
resource:
|
||||
type: "Microsoft.Compute/virtualMachines"
|
||||
name: "vm-win-identified"
|
||||
id: "/subscriptions/sub1/resourceGroups/rg1/providers/Microsoft.Compute/virtualMachines/vm-win-identified"
|
||||
location: "eastus"
|
||||
properties:
|
||||
storageProfile:
|
||||
imageReference:
|
||||
publisher: "MicrosoftWindowsServer"
|
||||
offer: "WindowsServer"
|
||||
sku: "2019-Datacenter"
|
||||
osDisk:
|
||||
osType: "Windows"
|
||||
osProfile:
|
||||
windowsConfiguration: {}
|
||||
request_context:
|
||||
apiVersion: "2024-01-01"
|
||||
host_await:
|
||||
- key:
|
||||
operation: "lookup_related_resources"
|
||||
type: "Microsoft.Compute/virtualMachines"
|
||||
response:
|
||||
identity:
|
||||
type: "UserAssigned"
|
||||
userAssignedIdentities:
|
||||
"/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/Built-In-Identity-RG/providers/Microsoft.ManagedIdentity/userAssignedIdentities/Built-In-Identity-eastus": {}
|
||||
want_undefined: true
|
||||
|
||||
# =========================================================================
|
||||
# DINE — Linux VM (Canonical publisher), no user-assigned identity
|
||||
# → existenceCondition fails → DeployIfNotExists
|
||||
# =========================================================================
|
||||
|
||||
- note: dine_linux_vm_no_identity
|
||||
resource:
|
||||
type: "Microsoft.Compute/virtualMachines"
|
||||
name: "vm-linux-noidentity"
|
||||
id: "/subscriptions/sub1/resourceGroups/rg1/providers/Microsoft.Compute/virtualMachines/vm-linux-noidentity"
|
||||
location: "eastus"
|
||||
properties:
|
||||
storageProfile:
|
||||
imageReference:
|
||||
publisher: "Canonical"
|
||||
offer: "UbuntuServer"
|
||||
sku: "18.04-LTS"
|
||||
osDisk:
|
||||
osType: "Linux"
|
||||
osProfile:
|
||||
linuxConfiguration: {}
|
||||
request_context:
|
||||
apiVersion: "2024-01-01"
|
||||
host_await:
|
||||
- key:
|
||||
operation: "lookup_related_resources"
|
||||
type: "Microsoft.Compute/virtualMachines"
|
||||
response: null
|
||||
want_effect: "DeployIfNotExists"
|
||||
|
||||
# =========================================================================
|
||||
# Pass — Wrong resource type (not a VM) → if condition fails
|
||||
# =========================================================================
|
||||
|
||||
- note: pass_wrong_type
|
||||
resource:
|
||||
type: "Microsoft.Storage/storageAccounts"
|
||||
name: "storageacct1"
|
||||
properties: {}
|
||||
want_undefined: true
|
||||
|
||||
# =========================================================================
|
||||
# Pass — Linux VM with microsoft-ads publisher and non-matching offer
|
||||
# → microsoft-ads specific conditions require offer like "linux*"
|
||||
# or in ["standard-data-science-vm","windows-data-science-vm"]
|
||||
# → Linux catch-all excludes microsoft-ads (it's in the notIn list)
|
||||
# → Windows catch-all fails (no windowsConfiguration, osType=Linux)
|
||||
# → if condition fails
|
||||
# =========================================================================
|
||||
|
||||
- note: pass_excluded_publisher
|
||||
resource:
|
||||
type: "Microsoft.Compute/virtualMachines"
|
||||
name: "vm-ads-excluded"
|
||||
location: "eastus"
|
||||
properties:
|
||||
storageProfile:
|
||||
imageReference:
|
||||
publisher: "microsoft-ads"
|
||||
offer: "some-random-offer"
|
||||
sku: "some-sku"
|
||||
osDisk:
|
||||
osType: "Linux"
|
||||
osProfile:
|
||||
linuxConfiguration: {}
|
||||
request_context:
|
||||
apiVersion: "2024-01-01"
|
||||
want_undefined: true
|
||||
|
||||
# =========================================================================
|
||||
# Pass — VM without osProfile and no matching publisher
|
||||
# → Windows catch-all: no windowsConfiguration, no Windows osType
|
||||
# → Linux catch-all: no linuxConfiguration, no Linux osType
|
||||
# → No specific publisher match
|
||||
# → if condition fails
|
||||
# =========================================================================
|
||||
|
||||
- note: pass_no_os_config
|
||||
resource:
|
||||
type: "Microsoft.Compute/virtualMachines"
|
||||
name: "vm-no-os"
|
||||
location: "eastus"
|
||||
properties:
|
||||
storageProfile:
|
||||
imageReference:
|
||||
publisher: "unknown-publisher"
|
||||
offer: "unknown-offer"
|
||||
sku: "unknown-sku"
|
||||
request_context:
|
||||
apiVersion: "2024-01-01"
|
||||
want_undefined: true
|
||||
127
tests/azure_policy/cases/e2e_keyvault_firewall_enabled.yaml
Normal file
127
tests/azure_policy/cases/e2e_keyvault_firewall_enabled.yaml
Normal file
@@ -0,0 +1,127 @@
|
||||
# Copyright (c) Microsoft Corporation.
|
||||
# Licensed under the MIT License.
|
||||
|
||||
# E2E Test: Key Vault/FirewallEnabled_Audit
|
||||
# Features: nested count + current() + ipRangeContains + parameterized effect/defaults
|
||||
|
||||
aliases: test_aliases.json
|
||||
|
||||
policy_definition: |
|
||||
{
|
||||
"properties": {
|
||||
"displayName": "Azure Key Vault should have firewall enabled",
|
||||
"policyType": "BuiltIn",
|
||||
"mode": "Indexed",
|
||||
"parameters": {
|
||||
"effect": {
|
||||
"type": "String",
|
||||
"allowedValues": ["Audit", "Deny", "Disabled"],
|
||||
"defaultValue": "Audit"
|
||||
},
|
||||
"restrictIPAddresses": {
|
||||
"type": "String",
|
||||
"defaultValue": "No",
|
||||
"allowedValues": ["Yes", "No"]
|
||||
},
|
||||
"allowedIPAddresses": {
|
||||
"type": "Array",
|
||||
"defaultValue": []
|
||||
}
|
||||
},
|
||||
"policyRule": {
|
||||
"if": {
|
||||
"allOf": [
|
||||
{"field": "type", "equals": "Microsoft.KeyVault/vaults"},
|
||||
{"field": "Microsoft.KeyVault/vaults/createMode", "notEquals": "recover"},
|
||||
{
|
||||
"anyOf": [
|
||||
{
|
||||
"field": "Microsoft.KeyVault/vaults/networkAcls.defaultAction",
|
||||
"notEquals": "Deny"
|
||||
},
|
||||
{
|
||||
"allOf": [
|
||||
{"value": "[parameters('restrictIPAddresses')]", "equals": "Yes"},
|
||||
{
|
||||
"anyOf": [
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"count": {
|
||||
"value": "[parameters('allowedIPAddresses')]",
|
||||
"name": "allowedIPAddresses"
|
||||
},
|
||||
"notEquals": 0
|
||||
},
|
||||
{
|
||||
"not": {
|
||||
"count": {
|
||||
"field": "Microsoft.KeyVault/vaults/networkAcls.ipRules[*]",
|
||||
"where": {
|
||||
"count": {
|
||||
"value": "[parameters('allowedIPAddresses')]",
|
||||
"name": "allowedIpAddress",
|
||||
"where": {
|
||||
"value": "[ipRangeContains(current('allowedIpAddress'), current('Microsoft.KeyVault/vaults/networkAcls.ipRules[*].value'))]",
|
||||
"equals": true
|
||||
}
|
||||
},
|
||||
"greater": 0
|
||||
}
|
||||
},
|
||||
"equals": "[length(field('Microsoft.KeyVault/vaults/networkAcls.ipRules[*]'))]"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"then": {
|
||||
"effect": "[parameters('effect')]"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cases:
|
||||
- note: audit_when_default_action_allow
|
||||
resource:
|
||||
type: "Microsoft.KeyVault/vaults"
|
||||
name: "kv-open"
|
||||
properties:
|
||||
networkAcls:
|
||||
defaultAction: "Allow"
|
||||
ipRules: []
|
||||
want_effect: "Audit"
|
||||
|
||||
- note: pass_when_default_action_deny_and_no_restriction_mode
|
||||
resource:
|
||||
type: "Microsoft.KeyVault/vaults"
|
||||
name: "kv-deny"
|
||||
properties:
|
||||
networkAcls:
|
||||
defaultAction: "Deny"
|
||||
ipRules: []
|
||||
want_undefined: true
|
||||
|
||||
- note: audit_when_restricted_allowed_ips_do_not_cover_all_rules
|
||||
resource:
|
||||
type: "Microsoft.KeyVault/vaults"
|
||||
name: "kv-partial"
|
||||
properties:
|
||||
networkAcls:
|
||||
defaultAction: "Deny"
|
||||
ipRules:
|
||||
- value: "10.0.0.5"
|
||||
- value: "192.168.1.5"
|
||||
parameters:
|
||||
restrictIPAddresses: "Yes"
|
||||
allowedIPAddresses: ["10.0.0.0/24"]
|
||||
effect: "Audit"
|
||||
want_effect: "Audit"
|
||||
326
tests/azure_policy/cases/e2e_managed_disk_encryption_sets.yaml
Normal file
326
tests/azure_policy/cases/e2e_managed_disk_encryption_sets.yaml
Normal file
@@ -0,0 +1,326 @@
|
||||
# Copyright (c) Microsoft Corporation.
|
||||
# Licensed under the MIT License.
|
||||
|
||||
# E2E Test: Compute/ManagedDiskEncryptionSetsAllowed_Deny
|
||||
# Real Azure Policy: "Managed disks should use a specific set of disk encryption sets for the customer-managed key encryption"
|
||||
# Features: anyOf, allOf nesting, field (type + alias), exists, notIn, in,
|
||||
# length(), count, not, multiple resource types
|
||||
# (VM, VMSS, disks, images, galleries/images/versions)
|
||||
|
||||
aliases: test_aliases.json
|
||||
|
||||
policy_definition: |
|
||||
{
|
||||
"properties": {
|
||||
"displayName": "Managed disks should use a specific set of disk encryption sets for the customer-managed key encryption",
|
||||
"policyType": "BuiltIn",
|
||||
"mode": "Indexed",
|
||||
"description": "Requiring a specific set of disk encryption sets to be used with managed disks give you control over the keys used for encryption at rest. You are able to select the allowed encrypted sets and all others are rejected when attached to a disk. Learn more at https://aka.ms/disks-cmk.",
|
||||
"metadata": {
|
||||
"category": "Compute",
|
||||
"version": "2.0.0"
|
||||
},
|
||||
"version": "2.0.0",
|
||||
"parameters": {
|
||||
"allowedEncryptionSets": {
|
||||
"type": "Array",
|
||||
"metadata": {
|
||||
"displayName": "Allowed disk encryption set",
|
||||
"description": "The list of allowed disk encryption sets for managed disks.",
|
||||
"strongType": "Microsoft.Compute/diskEncryptionSets"
|
||||
}
|
||||
},
|
||||
"effect": {
|
||||
"type": "string",
|
||||
"defaultValue": "Audit",
|
||||
"allowedValues": [
|
||||
"Audit",
|
||||
"Deny",
|
||||
"Disabled"
|
||||
],
|
||||
"metadata": {
|
||||
"displayName": "Effect",
|
||||
"description": "Enable or disable the execution of the policy"
|
||||
}
|
||||
}
|
||||
},
|
||||
"policyRule": {
|
||||
"if": {
|
||||
"anyOf": [
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"field": "type",
|
||||
"equals": "Microsoft.Compute/disks"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.Compute/disks/managedBy",
|
||||
"exists": "False"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.Compute/disks/encryption.diskEncryptionSetId",
|
||||
"notIn": "[parameters('allowedEncryptionSets')]"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"field": "type",
|
||||
"equals": "Microsoft.Compute/virtualMachines"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.Compute/virtualMachines/storageProfile.osDisk.managedDisk.diskEncryptionSet.id",
|
||||
"notIn": "[parameters('allowedEncryptionSets')]"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"field": "type",
|
||||
"equals": "Microsoft.Compute/virtualMachineScaleSets"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.Compute/virtualMachineScaleSets/virtualMachineProfile.storageProfile.osDisk.managedDisk.diskEncryptionSet.id",
|
||||
"notIn": "[parameters('allowedEncryptionSets')]"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"field": "type",
|
||||
"equals": "Microsoft.Compute/virtualMachineScaleSets"
|
||||
},
|
||||
{
|
||||
"count": {
|
||||
"field": "Microsoft.Compute/virtualMachineScaleSets/virtualMachineProfile.storageProfile.dataDisks[*]"
|
||||
},
|
||||
"greater": 0
|
||||
},
|
||||
{
|
||||
"not": {
|
||||
"field": "Microsoft.Compute/virtualMachineScaleSets/virtualMachineProfile.storageProfile.dataDisks[*].managedDisk.diskEncryptionSet.id",
|
||||
"in": "[parameters('allowedEncryptionSets')]"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"field": "type",
|
||||
"equals": "Microsoft.Compute/galleries/images/versions"
|
||||
},
|
||||
{
|
||||
"not": {
|
||||
"field": "Microsoft.Compute/galleries/images/versions/publishingProfile.targetRegions[*].encryption.osDiskImage.diskEncryptionSetId",
|
||||
"in": "[parameters('allowedEncryptionSets')]"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"field": "type",
|
||||
"equals": "Microsoft.Compute/galleries/images/versions"
|
||||
},
|
||||
{
|
||||
"value": "[length(field('Microsoft.Compute/galleries/images/versions/storageProfile.dataDiskImages[*]'))]",
|
||||
"greater": 0
|
||||
},
|
||||
{
|
||||
"not": {
|
||||
"field": "Microsoft.Compute/galleries/images/versions/publishingProfile.targetRegions[*].encryption.dataDiskImages[*].diskEncryptionSetId",
|
||||
"in": "[parameters('allowedEncryptionSets')]"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"field": "type",
|
||||
"equals": "Microsoft.Compute/images"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.Compute/images/storageProfile.osDisk.diskEncryptionSet.id",
|
||||
"notIn": "[parameters('allowedEncryptionSets')]"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"field": "type",
|
||||
"equals": "Microsoft.Compute/images"
|
||||
},
|
||||
{
|
||||
"value": "[length(field('Microsoft.Compute/images/storageProfile.dataDisks[*]'))]",
|
||||
"greater": 0
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.Compute/images/storageProfile.dataDisks[*].diskEncryptionSet.id",
|
||||
"notIn": "[parameters('allowedEncryptionSets')]"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"then": {
|
||||
"effect": "[parameters('effect')]"
|
||||
}
|
||||
},
|
||||
"versions": [
|
||||
"2.0.0"
|
||||
]
|
||||
},
|
||||
"id": "/providers/Microsoft.Authorization/policyDefinitions/d461a302-a187-421a-89ac-84acdb4edc04",
|
||||
"name": "d461a302-a187-421a-89ac-84acdb4edc04"
|
||||
}
|
||||
|
||||
cases:
|
||||
# =========================================================================
|
||||
# 1. VM with OS disk DES in allowed list → pass
|
||||
# =========================================================================
|
||||
|
||||
- note: pass_vm_osdisk_in_allowed
|
||||
parameters:
|
||||
effect: "Deny"
|
||||
allowedEncryptionSets:
|
||||
- "/subscriptions/sub1/resourceGroups/rg1/providers/Microsoft.Compute/diskEncryptionSets/des1"
|
||||
- "/subscriptions/sub1/resourceGroups/rg1/providers/Microsoft.Compute/diskEncryptionSets/des2"
|
||||
resource:
|
||||
type: "Microsoft.Compute/virtualMachines"
|
||||
name: "test-vm"
|
||||
properties:
|
||||
storageProfile:
|
||||
osDisk:
|
||||
managedDisk:
|
||||
diskEncryptionSet:
|
||||
id: "/subscriptions/sub1/resourceGroups/rg1/providers/Microsoft.Compute/diskEncryptionSets/des1"
|
||||
want_undefined: true
|
||||
|
||||
# =========================================================================
|
||||
# 2. VM with OS disk DES NOT in allowed list → Deny
|
||||
# =========================================================================
|
||||
|
||||
- note: deny_vm_osdisk_not_in_allowed
|
||||
parameters:
|
||||
effect: "Deny"
|
||||
allowedEncryptionSets:
|
||||
- "/subscriptions/sub1/resourceGroups/rg1/providers/Microsoft.Compute/diskEncryptionSets/des1"
|
||||
- "/subscriptions/sub1/resourceGroups/rg1/providers/Microsoft.Compute/diskEncryptionSets/des2"
|
||||
resource:
|
||||
type: "Microsoft.Compute/virtualMachines"
|
||||
name: "test-vm-bad"
|
||||
properties:
|
||||
storageProfile:
|
||||
osDisk:
|
||||
managedDisk:
|
||||
diskEncryptionSet:
|
||||
id: "/subscriptions/sub1/resourceGroups/rg1/providers/Microsoft.Compute/diskEncryptionSets/des-other"
|
||||
want_effect: "Deny"
|
||||
|
||||
# =========================================================================
|
||||
# 3. Managed disk with allowed DES → pass
|
||||
# =========================================================================
|
||||
|
||||
- note: pass_disk_in_allowed
|
||||
parameters:
|
||||
effect: "Deny"
|
||||
allowedEncryptionSets:
|
||||
- "/subscriptions/sub1/resourceGroups/rg1/providers/Microsoft.Compute/diskEncryptionSets/des1"
|
||||
- "/subscriptions/sub1/resourceGroups/rg1/providers/Microsoft.Compute/diskEncryptionSets/des2"
|
||||
resource:
|
||||
type: "Microsoft.Compute/disks"
|
||||
name: "test-disk-allowed"
|
||||
properties:
|
||||
diskSizeGB: 128
|
||||
encryption:
|
||||
diskEncryptionSetId: "/subscriptions/sub1/resourceGroups/rg1/providers/Microsoft.Compute/diskEncryptionSets/des1"
|
||||
type: "EncryptionAtRestWithCustomerKey"
|
||||
want_undefined: true
|
||||
|
||||
# =========================================================================
|
||||
# 4. Managed disk with disallowed DES → Deny
|
||||
# =========================================================================
|
||||
|
||||
- note: deny_disk_not_in_allowed
|
||||
parameters:
|
||||
effect: "Deny"
|
||||
allowedEncryptionSets:
|
||||
- "/subscriptions/sub1/resourceGroups/rg1/providers/Microsoft.Compute/diskEncryptionSets/des1"
|
||||
- "/subscriptions/sub1/resourceGroups/rg1/providers/Microsoft.Compute/diskEncryptionSets/des2"
|
||||
resource:
|
||||
type: "Microsoft.Compute/disks"
|
||||
name: "test-disk-bad"
|
||||
properties:
|
||||
diskSizeGB: 128
|
||||
encryption:
|
||||
diskEncryptionSetId: "/subscriptions/sub1/resourceGroups/rg1/providers/Microsoft.Compute/diskEncryptionSets/des-other"
|
||||
type: "EncryptionAtRestWithCustomerKey"
|
||||
want_effect: "Deny"
|
||||
|
||||
# =========================================================================
|
||||
# 5. Disk without managedBy (unmanaged) with disallowed DES → Deny
|
||||
# (managedBy does not exist, so the disk clause fires)
|
||||
# But if the DES is in the allowed list, it passes.
|
||||
# Here we test: disk without managedBy but WITH managedBy present → pass
|
||||
# (the disk clause requires managedBy exists=False)
|
||||
# =========================================================================
|
||||
|
||||
- note: pass_disk_unmanaged
|
||||
parameters:
|
||||
effect: "Deny"
|
||||
allowedEncryptionSets:
|
||||
- "/subscriptions/sub1/resourceGroups/rg1/providers/Microsoft.Compute/diskEncryptionSets/des1"
|
||||
resource:
|
||||
type: "Microsoft.Compute/disks"
|
||||
name: "test-disk-managed-by-vm"
|
||||
properties:
|
||||
managedBy: "/subscriptions/sub1/resourceGroups/rg1/providers/Microsoft.Compute/virtualMachines/myVM"
|
||||
diskSizeGB: 128
|
||||
encryption:
|
||||
diskEncryptionSetId: "/subscriptions/sub1/resourceGroups/rg1/providers/Microsoft.Compute/diskEncryptionSets/des-other"
|
||||
type: "EncryptionAtRestWithCustomerKey"
|
||||
want_undefined: true
|
||||
|
||||
# =========================================================================
|
||||
# 6. Wrong resource type → pass
|
||||
# =========================================================================
|
||||
|
||||
- note: pass_wrong_type
|
||||
parameters:
|
||||
effect: "Deny"
|
||||
allowedEncryptionSets:
|
||||
- "/subscriptions/sub1/resourceGroups/rg1/providers/Microsoft.Compute/diskEncryptionSets/des1"
|
||||
resource:
|
||||
type: "Microsoft.Storage/storageAccounts"
|
||||
name: "myStorage"
|
||||
properties: {}
|
||||
want_undefined: true
|
||||
|
||||
# =========================================================================
|
||||
# 7. Image with OS disk DES not in allowed list → Deny
|
||||
# =========================================================================
|
||||
|
||||
- note: deny_image_osdisk_not_allowed
|
||||
parameters:
|
||||
effect: "Deny"
|
||||
allowedEncryptionSets:
|
||||
- "/subscriptions/sub1/resourceGroups/rg1/providers/Microsoft.Compute/diskEncryptionSets/des1"
|
||||
- "/subscriptions/sub1/resourceGroups/rg1/providers/Microsoft.Compute/diskEncryptionSets/des2"
|
||||
resource:
|
||||
type: "Microsoft.Compute/images"
|
||||
name: "test-image-bad"
|
||||
properties:
|
||||
storageProfile:
|
||||
osDisk:
|
||||
osType: "Linux"
|
||||
osState: "Generalized"
|
||||
diskEncryptionSet:
|
||||
id: "/subscriptions/sub1/resourceGroups/rg1/providers/Microsoft.Compute/diskEncryptionSets/des-other"
|
||||
want_effect: "Deny"
|
||||
182
tests/azure_policy/cases/e2e_monitoring_dine_existence.yaml
Normal file
182
tests/azure_policy/cases/e2e_monitoring_dine_existence.yaml
Normal file
@@ -0,0 +1,182 @@
|
||||
# Copyright (c) Microsoft Corporation.
|
||||
# Licensed under the MIT License.
|
||||
|
||||
# E2E Test: Monitoring DINE existenceCondition — field + count + nested count
|
||||
# Reproduces the failure pattern from ServiceHealthSubscriptionLevelAlertRules_DINE
|
||||
#
|
||||
# Uses bare field names (no FQ alias prefix, no alias catalog) to test
|
||||
# existence logic independent of alias resolution.
|
||||
|
||||
policy_definition: |
|
||||
{
|
||||
"properties": {
|
||||
"displayName": "Test DINE existenceCondition",
|
||||
"policyType": "BuiltIn",
|
||||
"mode": "All",
|
||||
"parameters": {
|
||||
"effect": {
|
||||
"type": "String",
|
||||
"defaultValue": "DeployIfNotExists",
|
||||
"allowedValues": ["DeployIfNotExists", "AuditIfNotExists", "Disabled"]
|
||||
},
|
||||
"enableAlertRule": {
|
||||
"type": "String",
|
||||
"defaultValue": "true",
|
||||
"allowedValues": ["true", "false"]
|
||||
},
|
||||
"eventTypes": {
|
||||
"type": "Array",
|
||||
"defaultValue": ["Service Issues", "Planned Maintenance", "Health Advisories", "Security Advisories"]
|
||||
},
|
||||
"actionGroups": {
|
||||
"type": "Array",
|
||||
"defaultValue": []
|
||||
},
|
||||
"createNewActionGroup": {
|
||||
"type": "String",
|
||||
"defaultValue": "true",
|
||||
"allowedValues": ["true", "false"]
|
||||
},
|
||||
"newActionGroupName": {
|
||||
"type": "String",
|
||||
"defaultValue": "ag-ServiceHealthAlertActionGroup"
|
||||
},
|
||||
"resourceGroupName": {
|
||||
"type": "String",
|
||||
"defaultValue": "rg-serviceHealthAlert"
|
||||
}
|
||||
},
|
||||
"policyRule": {
|
||||
"if": {
|
||||
"field": "type",
|
||||
"equals": "Microsoft.Resources/subscriptions"
|
||||
},
|
||||
"then": {
|
||||
"effect": "[parameters('effect')]",
|
||||
"details": {
|
||||
"type": "Microsoft.Insights/ActivityLogAlerts",
|
||||
"existenceCondition": {
|
||||
"allOf": [
|
||||
{
|
||||
"field": "enabled",
|
||||
"equals": "[parameters('enableAlertRule')]"
|
||||
},
|
||||
{
|
||||
"count": {
|
||||
"field": "condition.allOf[*]",
|
||||
"where": {
|
||||
"allOf": [
|
||||
{
|
||||
"field": "condition.allOf[*].field",
|
||||
"equals": "category"
|
||||
},
|
||||
{
|
||||
"field": "condition.allOf[*].equals",
|
||||
"equals": "ServiceHealth"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"greaterOrEquals": 1
|
||||
},
|
||||
{
|
||||
"count": {
|
||||
"field": "condition.allOf[*].anyOf[*]",
|
||||
"where": {
|
||||
"field": "condition.allOf[*].anyOf[*].field",
|
||||
"equals": "properties.incidentType"
|
||||
}
|
||||
},
|
||||
"equals": "[if(contains(parameters('eventTypes'), 'Health Advisories'), add(length(parameters('eventTypes')), 2), length(parameters('eventTypes')))]"
|
||||
},
|
||||
{
|
||||
"count": {
|
||||
"field": "actions.actionGroups[*]"
|
||||
},
|
||||
"equals": "[add(length(parameters('actionGroups')), if(equals(parameters('createNewActionGroup'), 'true'), 1, 0))]"
|
||||
}
|
||||
]
|
||||
},
|
||||
"existenceScope": "resourceGroup",
|
||||
"roleDefinitionIds": [
|
||||
"/providers/Microsoft.Authorization/roleDefinitions/b24988ac-6180-42a0-ab88-20f7382dd24c"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cases:
|
||||
# =========================================================================
|
||||
# Related resource matches all conditions → compliant (Undefined)
|
||||
# =========================================================================
|
||||
- note: compliant_all_conditions
|
||||
resource:
|
||||
type: "Microsoft.Resources/subscriptions"
|
||||
id: "/subscriptions/00000000-0000-0000-0000-000000000000"
|
||||
parameters:
|
||||
effect: "AuditIfNotExists"
|
||||
host_await:
|
||||
- response:
|
||||
enabled: "true"
|
||||
scopes:
|
||||
- "/subscriptions/00000000-0000-0000-0000-000000000000"
|
||||
condition:
|
||||
allof:
|
||||
- field: "category"
|
||||
equals: "ServiceHealth"
|
||||
- anyof:
|
||||
- field: "properties.incidentType"
|
||||
equals: "Incident"
|
||||
- field: "properties.incidentType"
|
||||
equals: "Maintenance"
|
||||
- field: "properties.incidentType"
|
||||
equals: "Informational"
|
||||
- field: "properties.incidentType"
|
||||
equals: "ActionRequired"
|
||||
- field: "properties.incidentType"
|
||||
equals: "Security"
|
||||
- field: "properties.incidentType"
|
||||
equals: "Retirement"
|
||||
actions:
|
||||
actiongroups:
|
||||
- actiongroupid: "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/rg-serviceHealthAlert/providers/Microsoft.Insights/actionGroups/ag-ServiceHealthAlertActionGroup"
|
||||
want_undefined: true
|
||||
|
||||
# =========================================================================
|
||||
# Related resource not found → effect fires
|
||||
# =========================================================================
|
||||
- note: no_related_resource
|
||||
resource:
|
||||
type: "Microsoft.Resources/subscriptions"
|
||||
id: "/subscriptions/00000000-0000-0000-0000-000000000000"
|
||||
parameters:
|
||||
effect: "AuditIfNotExists"
|
||||
host_await:
|
||||
- response: null
|
||||
want_effect: "AuditIfNotExists"
|
||||
|
||||
# =========================================================================
|
||||
# Related resource found but enabled is false → effect fires
|
||||
# =========================================================================
|
||||
- note: enabled_false
|
||||
resource:
|
||||
type: "Microsoft.Resources/subscriptions"
|
||||
id: "/subscriptions/00000000-0000-0000-0000-000000000000"
|
||||
parameters:
|
||||
effect: "AuditIfNotExists"
|
||||
host_await:
|
||||
- response:
|
||||
enabled: "false"
|
||||
condition:
|
||||
allof:
|
||||
- field: "category"
|
||||
equals: "ServiceHealth"
|
||||
- anyof:
|
||||
- field: "properties.incidentType"
|
||||
equals: "Incident"
|
||||
actions:
|
||||
actiongroups:
|
||||
- actiongroupid: "something"
|
||||
want_effect: "AuditIfNotExists"
|
||||
138
tests/azure_policy/cases/e2e_nic_public_ip_deny.yaml
Normal file
138
tests/azure_policy/cases/e2e_nic_public_ip_deny.yaml
Normal file
@@ -0,0 +1,138 @@
|
||||
# Copyright (c) Microsoft Corporation.
|
||||
# Licensed under the MIT License.
|
||||
|
||||
# E2E Test: Network/NetworkPublicIPNic_Deny
|
||||
# Real Azure Policy: "Network interfaces should not have public IPs"
|
||||
# Source: regolator/policyDefinitions/Network/NetworkPublicIPNic_Deny.json
|
||||
#
|
||||
# Features exercised:
|
||||
# - Double negation pattern: not { field notLike "*" }
|
||||
# - Wildcard array alias: ipconfigurations[*].publicIpAddress.id
|
||||
# - No parameters (hardcoded deny effect)
|
||||
|
||||
aliases: test_aliases.json
|
||||
|
||||
policy_definition: |
|
||||
{
|
||||
"properties": {
|
||||
"displayName": "Network interfaces should not have public IPs",
|
||||
"policyType": "BuiltIn",
|
||||
"mode": "Indexed",
|
||||
"parameters": {},
|
||||
"policyRule": {
|
||||
"if": {
|
||||
"allOf": [
|
||||
{
|
||||
"field": "type",
|
||||
"equals": "Microsoft.Network/networkInterfaces"
|
||||
},
|
||||
{
|
||||
"not": {
|
||||
"field": "Microsoft.Network/networkInterfaces/ipconfigurations[*].publicIpAddress.id",
|
||||
"notLike": "*"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"then": {
|
||||
"effect": "deny"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cases:
|
||||
# =========================================================================
|
||||
# NIC with public IP → deny
|
||||
# =========================================================================
|
||||
|
||||
- note: deny_single_ip_config_with_public_ip
|
||||
resource:
|
||||
type: "Microsoft.Network/networkInterfaces"
|
||||
name: "nic-public"
|
||||
properties:
|
||||
ipConfigurations:
|
||||
- properties:
|
||||
privateIPAddress: "10.0.0.4"
|
||||
publicIpAddress:
|
||||
id: "/subscriptions/sub1/resourceGroups/rg/providers/Microsoft.Network/publicIPAddresses/pip1"
|
||||
want_effect: "deny"
|
||||
|
||||
- note: deny_multiple_ip_configs_all_public
|
||||
resource:
|
||||
type: "Microsoft.Network/networkInterfaces"
|
||||
name: "nic-multi-public"
|
||||
properties:
|
||||
ipConfigurations:
|
||||
- properties:
|
||||
privateIPAddress: "10.0.0.4"
|
||||
publicIpAddress:
|
||||
id: "/subscriptions/sub1/resourceGroups/rg/providers/Microsoft.Network/publicIPAddresses/pip1"
|
||||
- properties:
|
||||
privateIPAddress: "10.0.0.5"
|
||||
publicIpAddress:
|
||||
id: "/subscriptions/sub1/resourceGroups/rg/providers/Microsoft.Network/publicIPAddresses/pip2"
|
||||
want_effect: "deny"
|
||||
|
||||
- note: deny_one_of_many_has_public_ip
|
||||
resource:
|
||||
type: "Microsoft.Network/networkInterfaces"
|
||||
name: "nic-mixed"
|
||||
properties:
|
||||
ipConfigurations:
|
||||
- properties:
|
||||
privateIPAddress: "10.0.0.4"
|
||||
- properties:
|
||||
privateIPAddress: "10.0.0.5"
|
||||
publicIpAddress:
|
||||
id: "/subscriptions/sub1/resourceGroups/rg/providers/Microsoft.Network/publicIPAddresses/pip1"
|
||||
want_effect: "deny"
|
||||
|
||||
# =========================================================================
|
||||
# NIC without public IP → pass
|
||||
# =========================================================================
|
||||
|
||||
- note: pass_no_public_ip
|
||||
resource:
|
||||
type: "Microsoft.Network/networkInterfaces"
|
||||
name: "nic-private"
|
||||
properties:
|
||||
ipConfigurations:
|
||||
- properties:
|
||||
privateIPAddress: "10.0.0.4"
|
||||
want_undefined: true
|
||||
|
||||
- note: pass_multiple_configs_no_public_ip
|
||||
resource:
|
||||
type: "Microsoft.Network/networkInterfaces"
|
||||
name: "nic-multi-private"
|
||||
properties:
|
||||
ipConfigurations:
|
||||
- properties:
|
||||
privateIPAddress: "10.0.0.4"
|
||||
- properties:
|
||||
privateIPAddress: "10.0.0.5"
|
||||
want_undefined: true
|
||||
|
||||
- note: pass_empty_ip_configs
|
||||
resource:
|
||||
type: "Microsoft.Network/networkInterfaces"
|
||||
name: "nic-empty"
|
||||
properties:
|
||||
ipConfigurations: []
|
||||
want_undefined: true
|
||||
|
||||
# =========================================================================
|
||||
# Wrong resource type
|
||||
# =========================================================================
|
||||
|
||||
- note: pass_wrong_type
|
||||
resource:
|
||||
type: "Microsoft.Compute/virtualMachines"
|
||||
name: "not-a-nic"
|
||||
properties:
|
||||
ipConfigurations:
|
||||
- properties:
|
||||
publicIpAddress:
|
||||
id: "some-id"
|
||||
want_undefined: true
|
||||
370
tests/azure_policy/cases/e2e_nsg_rdp_access.yaml
Normal file
370
tests/azure_policy/cases/e2e_nsg_rdp_access.yaml
Normal file
@@ -0,0 +1,370 @@
|
||||
# Copyright (c) Microsoft Corporation.
|
||||
# Licensed under the MIT License.
|
||||
|
||||
# E2E Test: Network/NetworkSecurityGroup_RDPAccess_Audit
|
||||
# Real Azure Policy: "[Deprecated]: RDP access from the Internet should be blocked"
|
||||
# Features: and(), not(), lessOrEquals(), greaterOrEquals(), implicit allOf on [*]
|
||||
|
||||
aliases: test_aliases.json
|
||||
|
||||
policy_definition: |
|
||||
{
|
||||
"properties": {
|
||||
"displayName": "[Deprecated]: RDP access from the Internet should be blocked",
|
||||
"policyType": "BuiltIn",
|
||||
"mode": "All",
|
||||
"parameters": {
|
||||
"effect": {
|
||||
"type": "string",
|
||||
"defaultValue": "Audit",
|
||||
"allowedValues": ["Audit", "Disabled"]
|
||||
}
|
||||
},
|
||||
"policyRule": {
|
||||
"if": {
|
||||
"allOf": [
|
||||
{
|
||||
"field": "type",
|
||||
"equals": "Microsoft.Network/networkSecurityGroups/securityRules"
|
||||
},
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"field": "Microsoft.Network/networkSecurityGroups/securityRules/access",
|
||||
"equals": "Allow"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.Network/networkSecurityGroups/securityRules/direction",
|
||||
"equals": "Inbound"
|
||||
},
|
||||
{
|
||||
"anyOf": [
|
||||
{
|
||||
"field": "Microsoft.Network/networkSecurityGroups/securityRules/destinationPortRange",
|
||||
"equals": "*"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.Network/networkSecurityGroups/securityRules/destinationPortRange",
|
||||
"equals": "3389"
|
||||
},
|
||||
{
|
||||
"value": "[if(and(not(empty(field('Microsoft.Network/networkSecurityGroups/securityRules/destinationPortRange'))), contains(field('Microsoft.Network/networkSecurityGroups/securityRules/destinationPortRange'),'-')), and(lessOrEquals(int(first(split(field('Microsoft.Network/networkSecurityGroups/securityRules/destinationPortRange'), '-'))),3389),greaterOrEquals(int(last(split(field('Microsoft.Network/networkSecurityGroups/securityRules/destinationPortRange'), '-'))),3389)), 'false')]",
|
||||
"equals": "true"
|
||||
},
|
||||
{
|
||||
"count": {
|
||||
"field": "Microsoft.Network/networkSecurityGroups/securityRules/destinationPortRanges[*]",
|
||||
"where": {
|
||||
"value": "[if(and(not(empty(first(field('Microsoft.Network/networkSecurityGroups/securityRules/destinationPortRanges[*]')))), contains(first(field('Microsoft.Network/networkSecurityGroups/securityRules/destinationPortRanges[*]')),'-')), and(lessOrEquals(int(first(split(first(field('Microsoft.Network/networkSecurityGroups/securityRules/destinationPortRanges[*]')), '-'))),3389),greaterOrEquals(int(last(split(first(field('Microsoft.Network/networkSecurityGroups/securityRules/destinationPortRanges[*]')), '-'))),3389)) , 'false')]",
|
||||
"equals": "true"
|
||||
}
|
||||
},
|
||||
"greater": 0
|
||||
},
|
||||
{
|
||||
"not": {
|
||||
"field": "Microsoft.Network/networkSecurityGroups/securityRules/destinationPortRanges[*]",
|
||||
"notEquals": "*"
|
||||
}
|
||||
},
|
||||
{
|
||||
"not": {
|
||||
"field": "Microsoft.Network/networkSecurityGroups/securityRules/destinationPortRanges[*]",
|
||||
"notEquals": "3389"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"anyOf": [
|
||||
{
|
||||
"field": "Microsoft.Network/networkSecurityGroups/securityRules/sourceAddressPrefix",
|
||||
"equals": "*"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.Network/networkSecurityGroups/securityRules/sourceAddressPrefix",
|
||||
"equals": "Internet"
|
||||
},
|
||||
{
|
||||
"not": {
|
||||
"field": "Microsoft.Network/networkSecurityGroups/securityRules/sourceAddressPrefixes[*]",
|
||||
"notEquals": "*"
|
||||
}
|
||||
},
|
||||
{
|
||||
"not": {
|
||||
"field": "Microsoft.Network/networkSecurityGroups/securityRules/sourceAddressPrefixes[*]",
|
||||
"notEquals": "Internet"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"then": {
|
||||
"effect": "[parameters('effect')]"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cases:
|
||||
# =========================================================================
|
||||
# Exact port 3389 match
|
||||
# =========================================================================
|
||||
|
||||
- note: audit_exact_port_3389_from_internet
|
||||
resource:
|
||||
type: "Microsoft.Network/networkSecurityGroups/securityRules"
|
||||
name: "allow-rdp"
|
||||
properties:
|
||||
access: "Allow"
|
||||
direction: "Inbound"
|
||||
destinationPortRange: "3389"
|
||||
sourceAddressPrefix: "Internet"
|
||||
want_effect: "Audit"
|
||||
|
||||
- note: audit_exact_port_3389_from_wildcard
|
||||
resource:
|
||||
type: "Microsoft.Network/networkSecurityGroups/securityRules"
|
||||
name: "allow-rdp-any"
|
||||
properties:
|
||||
access: "Allow"
|
||||
direction: "Inbound"
|
||||
destinationPortRange: "3389"
|
||||
sourceAddressPrefix: "*"
|
||||
want_effect: "Audit"
|
||||
|
||||
# =========================================================================
|
||||
# Wildcard port (*)
|
||||
# =========================================================================
|
||||
|
||||
- note: audit_wildcard_port_from_internet
|
||||
resource:
|
||||
type: "Microsoft.Network/networkSecurityGroups/securityRules"
|
||||
name: "allow-all"
|
||||
properties:
|
||||
access: "Allow"
|
||||
direction: "Inbound"
|
||||
destinationPortRange: "*"
|
||||
sourceAddressPrefix: "Internet"
|
||||
want_effect: "Audit"
|
||||
|
||||
# =========================================================================
|
||||
# Port range containing 3389
|
||||
# =========================================================================
|
||||
|
||||
- note: audit_port_range_includes_3389
|
||||
resource:
|
||||
type: "Microsoft.Network/networkSecurityGroups/securityRules"
|
||||
name: "allow-rdp-range"
|
||||
properties:
|
||||
access: "Allow"
|
||||
direction: "Inbound"
|
||||
destinationPortRange: "3380-3390"
|
||||
sourceAddressPrefix: "*"
|
||||
want_effect: "Audit"
|
||||
|
||||
- note: audit_port_range_exact_3389_to_3389
|
||||
resource:
|
||||
type: "Microsoft.Network/networkSecurityGroups/securityRules"
|
||||
name: "exactly-3389"
|
||||
properties:
|
||||
access: "Allow"
|
||||
direction: "Inbound"
|
||||
destinationPortRange: "3389-3389"
|
||||
sourceAddressPrefix: "Internet"
|
||||
want_effect: "Audit"
|
||||
|
||||
- note: audit_port_range_1_to_4000
|
||||
resource:
|
||||
type: "Microsoft.Network/networkSecurityGroups/securityRules"
|
||||
name: "low-ports"
|
||||
properties:
|
||||
access: "Allow"
|
||||
direction: "Inbound"
|
||||
destinationPortRange: "1-4000"
|
||||
sourceAddressPrefix: "*"
|
||||
want_effect: "Audit"
|
||||
|
||||
# =========================================================================
|
||||
# Port range NOT containing 3389
|
||||
# =========================================================================
|
||||
|
||||
- note: pass_port_range_excludes_3389
|
||||
resource:
|
||||
type: "Microsoft.Network/networkSecurityGroups/securityRules"
|
||||
name: "http-only"
|
||||
properties:
|
||||
access: "Allow"
|
||||
direction: "Inbound"
|
||||
destinationPortRange: "80-443"
|
||||
destinationPortRanges: []
|
||||
sourceAddressPrefix: "Internet"
|
||||
sourceAddressPrefixes: []
|
||||
want_undefined: true
|
||||
|
||||
- note: pass_port_3390_only
|
||||
resource:
|
||||
type: "Microsoft.Network/networkSecurityGroups/securityRules"
|
||||
name: "port-3390"
|
||||
properties:
|
||||
access: "Allow"
|
||||
direction: "Inbound"
|
||||
destinationPortRange: "3390"
|
||||
destinationPortRanges: []
|
||||
sourceAddressPrefix: "*"
|
||||
sourceAddressPrefixes: []
|
||||
want_undefined: true
|
||||
|
||||
# =========================================================================
|
||||
# destinationPortRanges[*] array — double negation pattern
|
||||
# =========================================================================
|
||||
|
||||
- note: audit_port_ranges_array_contains_3389
|
||||
resource:
|
||||
type: "Microsoft.Network/networkSecurityGroups/securityRules"
|
||||
name: "multi-port-rdp"
|
||||
properties:
|
||||
access: "Allow"
|
||||
direction: "Inbound"
|
||||
destinationPortRanges:
|
||||
- "80"
|
||||
- "3389"
|
||||
- "443"
|
||||
sourceAddressPrefix: "Internet"
|
||||
want_effect: "Audit"
|
||||
|
||||
- note: audit_port_ranges_array_contains_wildcard
|
||||
resource:
|
||||
type: "Microsoft.Network/networkSecurityGroups/securityRules"
|
||||
name: "multi-port-wildcard"
|
||||
properties:
|
||||
access: "Allow"
|
||||
direction: "Inbound"
|
||||
destinationPortRanges:
|
||||
- "*"
|
||||
sourceAddressPrefix: "*"
|
||||
want_effect: "Audit"
|
||||
|
||||
- note: audit_port_ranges_array_with_range_containing_3389
|
||||
resource:
|
||||
type: "Microsoft.Network/networkSecurityGroups/securityRules"
|
||||
name: "multi-port-range"
|
||||
properties:
|
||||
access: "Allow"
|
||||
direction: "Inbound"
|
||||
destinationPortRanges:
|
||||
- "80-443"
|
||||
- "3380-3390"
|
||||
sourceAddressPrefix: "Internet"
|
||||
want_effect: "Audit"
|
||||
|
||||
- note: pass_port_ranges_array_no_3389
|
||||
resource:
|
||||
type: "Microsoft.Network/networkSecurityGroups/securityRules"
|
||||
name: "non-rdp-ports"
|
||||
properties:
|
||||
access: "Allow"
|
||||
direction: "Inbound"
|
||||
destinationPortRanges:
|
||||
- "80"
|
||||
- "443"
|
||||
- "8080"
|
||||
sourceAddressPrefix: "Internet"
|
||||
want_undefined: true
|
||||
|
||||
# =========================================================================
|
||||
# Source address variations
|
||||
# =========================================================================
|
||||
|
||||
- note: audit_source_prefixes_array_wildcard
|
||||
resource:
|
||||
type: "Microsoft.Network/networkSecurityGroups/securityRules"
|
||||
name: "src-wild-array"
|
||||
properties:
|
||||
access: "Allow"
|
||||
direction: "Inbound"
|
||||
destinationPortRange: "3389"
|
||||
sourceAddressPrefixes:
|
||||
- "*"
|
||||
want_effect: "Audit"
|
||||
|
||||
- note: audit_source_prefixes_array_internet
|
||||
resource:
|
||||
type: "Microsoft.Network/networkSecurityGroups/securityRules"
|
||||
name: "src-inet-array"
|
||||
properties:
|
||||
access: "Allow"
|
||||
direction: "Inbound"
|
||||
destinationPortRange: "3389"
|
||||
sourceAddressPrefixes:
|
||||
- "Internet"
|
||||
want_effect: "Audit"
|
||||
|
||||
- note: pass_source_is_private_subnet
|
||||
resource:
|
||||
type: "Microsoft.Network/networkSecurityGroups/securityRules"
|
||||
name: "private-rdp"
|
||||
properties:
|
||||
access: "Allow"
|
||||
direction: "Inbound"
|
||||
destinationPortRange: "3389"
|
||||
destinationPortRanges: []
|
||||
sourceAddressPrefix: "10.0.0.0/8"
|
||||
sourceAddressPrefixes: []
|
||||
want_undefined: true
|
||||
|
||||
- note: pass_source_prefixes_all_private
|
||||
resource:
|
||||
type: "Microsoft.Network/networkSecurityGroups/securityRules"
|
||||
name: "private-array"
|
||||
properties:
|
||||
access: "Allow"
|
||||
direction: "Inbound"
|
||||
destinationPortRange: "3389"
|
||||
destinationPortRanges: []
|
||||
sourceAddressPrefixes:
|
||||
- "10.0.0.0/8"
|
||||
- "172.16.0.0/12"
|
||||
want_undefined: true
|
||||
|
||||
# =========================================================================
|
||||
# Non-matching access / direction
|
||||
# =========================================================================
|
||||
|
||||
- note: pass_deny_rule
|
||||
resource:
|
||||
type: "Microsoft.Network/networkSecurityGroups/securityRules"
|
||||
name: "deny-rdp"
|
||||
properties:
|
||||
access: "Deny"
|
||||
direction: "Inbound"
|
||||
destinationPortRange: "3389"
|
||||
sourceAddressPrefix: "*"
|
||||
want_undefined: true
|
||||
|
||||
- note: pass_outbound_rule
|
||||
resource:
|
||||
type: "Microsoft.Network/networkSecurityGroups/securityRules"
|
||||
name: "outbound-rdp"
|
||||
properties:
|
||||
access: "Allow"
|
||||
direction: "Outbound"
|
||||
destinationPortRange: "3389"
|
||||
sourceAddressPrefix: "*"
|
||||
want_undefined: true
|
||||
|
||||
# =========================================================================
|
||||
# Wrong resource type
|
||||
# =========================================================================
|
||||
|
||||
- note: pass_wrong_type
|
||||
resource:
|
||||
type: "Microsoft.Storage/storageAccounts"
|
||||
name: "not-nsg"
|
||||
properties:
|
||||
access: "Allow"
|
||||
want_undefined: true
|
||||
379
tests/azure_policy/cases/e2e_nsg_ssh_access.yaml
Normal file
379
tests/azure_policy/cases/e2e_nsg_ssh_access.yaml
Normal file
@@ -0,0 +1,379 @@
|
||||
# Copyright (c) Microsoft Corporation.
|
||||
# Licensed under the MIT License.
|
||||
|
||||
# E2E Test: Network/NetworkSecurityGroup_SSHAccess_Audit
|
||||
# Real Azure Policy: "[Deprecated]: SSH access from the Internet should be blocked"
|
||||
# Source: regolator/policyDefinitions/Network/NetworkSecurityGroup_SSHAccess_Audit.json
|
||||
#
|
||||
# Features exercised:
|
||||
# - Deeply nested template expressions: if(and(not(empty(...)), contains(...)))
|
||||
# - Arithmetic in templates: int(), split(), first(), last()
|
||||
# - Port range parsing: lessOrEquals/greaterOrEquals on split results
|
||||
# - count with where clause + template expression
|
||||
# - Double negation pattern: not { field notEquals "x" }
|
||||
# - Parameterized effect with defaultValue
|
||||
# - Multiple anyOf branches (destination port + source address)
|
||||
|
||||
aliases: test_aliases.json
|
||||
|
||||
policy_definition: |
|
||||
{
|
||||
"properties": {
|
||||
"displayName": "[Deprecated]: SSH access from the Internet should be blocked",
|
||||
"policyType": "BuiltIn",
|
||||
"mode": "All",
|
||||
"parameters": {
|
||||
"effect": {
|
||||
"type": "string",
|
||||
"defaultValue": "Audit",
|
||||
"allowedValues": ["Audit", "Disabled"]
|
||||
}
|
||||
},
|
||||
"policyRule": {
|
||||
"if": {
|
||||
"allOf": [
|
||||
{
|
||||
"field": "type",
|
||||
"equals": "Microsoft.Network/networkSecurityGroups/securityRules"
|
||||
},
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"field": "Microsoft.Network/networkSecurityGroups/securityRules/access",
|
||||
"equals": "Allow"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.Network/networkSecurityGroups/securityRules/direction",
|
||||
"equals": "Inbound"
|
||||
},
|
||||
{
|
||||
"anyOf": [
|
||||
{
|
||||
"field": "Microsoft.Network/networkSecurityGroups/securityRules/destinationPortRange",
|
||||
"equals": "*"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.Network/networkSecurityGroups/securityRules/destinationPortRange",
|
||||
"equals": "22"
|
||||
},
|
||||
{
|
||||
"value": "[if(and(not(empty(field('Microsoft.Network/networkSecurityGroups/securityRules/destinationPortRange'))), contains(field('Microsoft.Network/networkSecurityGroups/securityRules/destinationPortRange'),'-')), and(lessOrEquals(int(first(split(field('Microsoft.Network/networkSecurityGroups/securityRules/destinationPortRange'), '-'))),22),greaterOrEquals(int(last(split(field('Microsoft.Network/networkSecurityGroups/securityRules/destinationPortRange'), '-'))),22)), 'false')]",
|
||||
"equals": "true"
|
||||
},
|
||||
{
|
||||
"count": {
|
||||
"field": "Microsoft.Network/networkSecurityGroups/securityRules/destinationPortRanges[*]",
|
||||
"where": {
|
||||
"value": "[if(and(not(empty(first(field('Microsoft.Network/networkSecurityGroups/securityRules/destinationPortRanges[*]')))), contains(first(field('Microsoft.Network/networkSecurityGroups/securityRules/destinationPortRanges[*]')),'-')), and(lessOrEquals(int(first(split(first(field('Microsoft.Network/networkSecurityGroups/securityRules/destinationPortRanges[*]')), '-'))),22),greaterOrEquals(int(last(split(first(field('Microsoft.Network/networkSecurityGroups/securityRules/destinationPortRanges[*]')), '-'))),22)) , 'false')]",
|
||||
"equals": "true"
|
||||
}
|
||||
},
|
||||
"greater": 0
|
||||
},
|
||||
{
|
||||
"not": {
|
||||
"field": "Microsoft.Network/networkSecurityGroups/securityRules/destinationPortRanges[*]",
|
||||
"notEquals": "*"
|
||||
}
|
||||
},
|
||||
{
|
||||
"not": {
|
||||
"field": "Microsoft.Network/networkSecurityGroups/securityRules/destinationPortRanges[*]",
|
||||
"notEquals": "22"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"anyOf": [
|
||||
{
|
||||
"field": "Microsoft.Network/networkSecurityGroups/securityRules/sourceAddressPrefix",
|
||||
"equals": "*"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.Network/networkSecurityGroups/securityRules/sourceAddressPrefix",
|
||||
"equals": "Internet"
|
||||
},
|
||||
{
|
||||
"not": {
|
||||
"field": "Microsoft.Network/networkSecurityGroups/securityRules/sourceAddressPrefixes[*]",
|
||||
"notEquals": "*"
|
||||
}
|
||||
},
|
||||
{
|
||||
"not": {
|
||||
"field": "Microsoft.Network/networkSecurityGroups/securityRules/sourceAddressPrefixes[*]",
|
||||
"notEquals": "Internet"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"then": {
|
||||
"effect": "[parameters('effect')]"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cases:
|
||||
# =========================================================================
|
||||
# Exact port 22 match
|
||||
# =========================================================================
|
||||
|
||||
- note: audit_exact_port_22_from_internet
|
||||
resource:
|
||||
type: "Microsoft.Network/networkSecurityGroups/securityRules"
|
||||
name: "allow-ssh"
|
||||
properties:
|
||||
access: "Allow"
|
||||
direction: "Inbound"
|
||||
destinationPortRange: "22"
|
||||
sourceAddressPrefix: "Internet"
|
||||
want_effect: "Audit"
|
||||
|
||||
- note: audit_exact_port_22_from_wildcard
|
||||
resource:
|
||||
type: "Microsoft.Network/networkSecurityGroups/securityRules"
|
||||
name: "allow-ssh-any"
|
||||
properties:
|
||||
access: "Allow"
|
||||
direction: "Inbound"
|
||||
destinationPortRange: "22"
|
||||
sourceAddressPrefix: "*"
|
||||
want_effect: "Audit"
|
||||
|
||||
# =========================================================================
|
||||
# Wildcard port (*)
|
||||
# =========================================================================
|
||||
|
||||
- note: audit_wildcard_port_from_internet
|
||||
resource:
|
||||
type: "Microsoft.Network/networkSecurityGroups/securityRules"
|
||||
name: "allow-all"
|
||||
properties:
|
||||
access: "Allow"
|
||||
direction: "Inbound"
|
||||
destinationPortRange: "*"
|
||||
sourceAddressPrefix: "Internet"
|
||||
want_effect: "Audit"
|
||||
|
||||
# =========================================================================
|
||||
# Port range containing 22
|
||||
# =========================================================================
|
||||
|
||||
- note: audit_port_range_includes_22
|
||||
resource:
|
||||
type: "Microsoft.Network/networkSecurityGroups/securityRules"
|
||||
name: "allow-ssh-range"
|
||||
properties:
|
||||
access: "Allow"
|
||||
direction: "Inbound"
|
||||
destinationPortRange: "20-25"
|
||||
sourceAddressPrefix: "*"
|
||||
want_effect: "Audit"
|
||||
|
||||
- note: audit_port_range_exact_22_to_22
|
||||
resource:
|
||||
type: "Microsoft.Network/networkSecurityGroups/securityRules"
|
||||
name: "exactly-22"
|
||||
properties:
|
||||
access: "Allow"
|
||||
direction: "Inbound"
|
||||
destinationPortRange: "22-22"
|
||||
sourceAddressPrefix: "Internet"
|
||||
want_effect: "Audit"
|
||||
|
||||
- note: audit_port_range_1_to_1024
|
||||
resource:
|
||||
type: "Microsoft.Network/networkSecurityGroups/securityRules"
|
||||
name: "low-ports"
|
||||
properties:
|
||||
access: "Allow"
|
||||
direction: "Inbound"
|
||||
destinationPortRange: "1-1024"
|
||||
sourceAddressPrefix: "*"
|
||||
want_effect: "Audit"
|
||||
|
||||
# =========================================================================
|
||||
# Port range NOT containing 22
|
||||
# =========================================================================
|
||||
|
||||
- note: pass_port_range_excludes_22
|
||||
resource:
|
||||
type: "Microsoft.Network/networkSecurityGroups/securityRules"
|
||||
name: "http-only"
|
||||
properties:
|
||||
access: "Allow"
|
||||
direction: "Inbound"
|
||||
destinationPortRange: "80-443"
|
||||
destinationPortRanges: []
|
||||
sourceAddressPrefix: "Internet"
|
||||
sourceAddressPrefixes: []
|
||||
want_undefined: true
|
||||
|
||||
- note: pass_port_23_only
|
||||
resource:
|
||||
type: "Microsoft.Network/networkSecurityGroups/securityRules"
|
||||
name: "port-23"
|
||||
properties:
|
||||
access: "Allow"
|
||||
direction: "Inbound"
|
||||
destinationPortRange: "23"
|
||||
destinationPortRanges: []
|
||||
sourceAddressPrefix: "*"
|
||||
sourceAddressPrefixes: []
|
||||
want_undefined: true
|
||||
|
||||
# =========================================================================
|
||||
# destinationPortRanges[*] array — double negation pattern
|
||||
# =========================================================================
|
||||
|
||||
- note: audit_port_ranges_array_contains_22
|
||||
resource:
|
||||
type: "Microsoft.Network/networkSecurityGroups/securityRules"
|
||||
name: "multi-port-ssh"
|
||||
properties:
|
||||
access: "Allow"
|
||||
direction: "Inbound"
|
||||
destinationPortRanges:
|
||||
- "80"
|
||||
- "22"
|
||||
- "443"
|
||||
sourceAddressPrefix: "Internet"
|
||||
want_effect: "Audit"
|
||||
|
||||
- note: audit_port_ranges_array_contains_wildcard
|
||||
resource:
|
||||
type: "Microsoft.Network/networkSecurityGroups/securityRules"
|
||||
name: "multi-port-wildcard"
|
||||
properties:
|
||||
access: "Allow"
|
||||
direction: "Inbound"
|
||||
destinationPortRanges:
|
||||
- "*"
|
||||
sourceAddressPrefix: "*"
|
||||
want_effect: "Audit"
|
||||
|
||||
- note: audit_port_ranges_array_with_range_containing_22
|
||||
resource:
|
||||
type: "Microsoft.Network/networkSecurityGroups/securityRules"
|
||||
name: "multi-port-range"
|
||||
properties:
|
||||
access: "Allow"
|
||||
direction: "Inbound"
|
||||
destinationPortRanges:
|
||||
- "80-443"
|
||||
- "10-30"
|
||||
sourceAddressPrefix: "Internet"
|
||||
want_effect: "Audit"
|
||||
|
||||
- note: pass_port_ranges_array_no_22
|
||||
resource:
|
||||
type: "Microsoft.Network/networkSecurityGroups/securityRules"
|
||||
name: "non-ssh-ports"
|
||||
properties:
|
||||
access: "Allow"
|
||||
direction: "Inbound"
|
||||
destinationPortRanges:
|
||||
- "80"
|
||||
- "443"
|
||||
- "8080"
|
||||
sourceAddressPrefix: "Internet"
|
||||
want_undefined: true
|
||||
|
||||
# =========================================================================
|
||||
# Source address variations
|
||||
# =========================================================================
|
||||
|
||||
- note: audit_source_prefixes_array_wildcard
|
||||
resource:
|
||||
type: "Microsoft.Network/networkSecurityGroups/securityRules"
|
||||
name: "src-wild-array"
|
||||
properties:
|
||||
access: "Allow"
|
||||
direction: "Inbound"
|
||||
destinationPortRange: "22"
|
||||
sourceAddressPrefixes:
|
||||
- "*"
|
||||
want_effect: "Audit"
|
||||
|
||||
- note: audit_source_prefixes_array_internet
|
||||
resource:
|
||||
type: "Microsoft.Network/networkSecurityGroups/securityRules"
|
||||
name: "src-inet-array"
|
||||
properties:
|
||||
access: "Allow"
|
||||
direction: "Inbound"
|
||||
destinationPortRange: "22"
|
||||
sourceAddressPrefixes:
|
||||
- "Internet"
|
||||
want_effect: "Audit"
|
||||
|
||||
- note: pass_source_is_private_subnet
|
||||
resource:
|
||||
type: "Microsoft.Network/networkSecurityGroups/securityRules"
|
||||
name: "private-ssh"
|
||||
properties:
|
||||
access: "Allow"
|
||||
direction: "Inbound"
|
||||
destinationPortRange: "22"
|
||||
destinationPortRanges: []
|
||||
sourceAddressPrefix: "10.0.0.0/8"
|
||||
sourceAddressPrefixes: []
|
||||
want_undefined: true
|
||||
|
||||
- note: pass_source_prefixes_all_private
|
||||
resource:
|
||||
type: "Microsoft.Network/networkSecurityGroups/securityRules"
|
||||
name: "private-array"
|
||||
properties:
|
||||
access: "Allow"
|
||||
direction: "Inbound"
|
||||
destinationPortRange: "22"
|
||||
destinationPortRanges: []
|
||||
sourceAddressPrefixes:
|
||||
- "10.0.0.0/8"
|
||||
- "172.16.0.0/12"
|
||||
want_undefined: true
|
||||
|
||||
# =========================================================================
|
||||
# Non-matching access / direction
|
||||
# =========================================================================
|
||||
|
||||
- note: pass_deny_rule
|
||||
resource:
|
||||
type: "Microsoft.Network/networkSecurityGroups/securityRules"
|
||||
name: "deny-ssh"
|
||||
properties:
|
||||
access: "Deny"
|
||||
direction: "Inbound"
|
||||
destinationPortRange: "22"
|
||||
sourceAddressPrefix: "*"
|
||||
want_undefined: true
|
||||
|
||||
- note: pass_outbound_rule
|
||||
resource:
|
||||
type: "Microsoft.Network/networkSecurityGroups/securityRules"
|
||||
name: "outbound-ssh"
|
||||
properties:
|
||||
access: "Allow"
|
||||
direction: "Outbound"
|
||||
destinationPortRange: "22"
|
||||
sourceAddressPrefix: "*"
|
||||
want_undefined: true
|
||||
|
||||
# =========================================================================
|
||||
# Wrong resource type
|
||||
# =========================================================================
|
||||
|
||||
- note: pass_wrong_type
|
||||
resource:
|
||||
type: "Microsoft.Storage/storageAccounts"
|
||||
name: "not-nsg"
|
||||
properties:
|
||||
access: "Allow"
|
||||
want_undefined: true
|
||||
121
tests/azure_policy/cases/e2e_pg_audit_aine.yaml
Normal file
121
tests/azure_policy/cases/e2e_pg_audit_aine.yaml
Normal file
@@ -0,0 +1,121 @@
|
||||
# Copyright (c) Microsoft Corporation.
|
||||
# Licensed under the MIT License.
|
||||
|
||||
# E2E Test: PostgreSQL/FlexibleServers_EnablePgAudit_AINE
|
||||
# Real Azure Policy: "Auditing with PgAudit should be enabled for PostgreSQL
|
||||
# flexible servers"
|
||||
# Source: regolator/policyDefinitions/PostgreSQL/FlexibleServers_EnablePgAudit_AINE.json
|
||||
#
|
||||
# Features exercised:
|
||||
# - AuditIfNotExists with simple existenceCondition (notEquals)
|
||||
# - Sub-resource type: Microsoft.DBforPostgreSQL/flexibleServers/configurations
|
||||
# - host_await for cross-resource lookup
|
||||
|
||||
aliases: test_aliases.json
|
||||
|
||||
policy_definition: |
|
||||
{
|
||||
"properties": {
|
||||
"displayName": "Auditing with PgAudit should be enabled for PostgreSQL flexible servers",
|
||||
"policyType": "BuiltIn",
|
||||
"mode": "Indexed",
|
||||
"parameters": {
|
||||
"effect": {
|
||||
"type": "string",
|
||||
"defaultValue": "AuditIfNotExists",
|
||||
"allowedValues": ["AuditIfNotExists", "Disabled"]
|
||||
}
|
||||
},
|
||||
"policyRule": {
|
||||
"if": {
|
||||
"field": "type",
|
||||
"equals": "Microsoft.DBforPostgreSQL/flexibleServers"
|
||||
},
|
||||
"then": {
|
||||
"effect": "[parameters('effect')]",
|
||||
"details": {
|
||||
"type": "Microsoft.DBforPostgreSQL/flexibleServers/configurations",
|
||||
"name": "pgaudit.log",
|
||||
"existenceCondition": {
|
||||
"field": "Microsoft.DBforPostgreSQL/flexibleServers/configurations/value",
|
||||
"notEquals": "none"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cases:
|
||||
# =========================================================================
|
||||
# Related resource not found → AuditIfNotExists
|
||||
# =========================================================================
|
||||
|
||||
- note: aine_config_not_found
|
||||
resource:
|
||||
type: "Microsoft.DBforPostgreSQL/flexibleServers"
|
||||
name: "pg-no-config"
|
||||
properties: {}
|
||||
host_await:
|
||||
- key:
|
||||
operation: "lookup_related_resources"
|
||||
type: "Microsoft.DBforPostgreSQL/flexibleServers/configurations"
|
||||
name: "pgaudit.log"
|
||||
response: null
|
||||
want_effect: "AuditIfNotExists"
|
||||
|
||||
# =========================================================================
|
||||
# pgaudit.log value is "all" → compliant
|
||||
# =========================================================================
|
||||
|
||||
- note: compliant_pgaudit_enabled
|
||||
resource:
|
||||
type: "Microsoft.DBforPostgreSQL/flexibleServers"
|
||||
name: "pg-audit-on"
|
||||
properties: {}
|
||||
host_await:
|
||||
- response:
|
||||
properties:
|
||||
value: "all"
|
||||
want_undefined: true
|
||||
|
||||
# =========================================================================
|
||||
# pgaudit.log value is "none" → non-compliant (notEquals "none" fails)
|
||||
# =========================================================================
|
||||
|
||||
- note: aine_pgaudit_none
|
||||
resource:
|
||||
type: "Microsoft.DBforPostgreSQL/flexibleServers"
|
||||
name: "pg-audit-off"
|
||||
properties: {}
|
||||
host_await:
|
||||
- response:
|
||||
properties:
|
||||
value: "none"
|
||||
want_effect: "AuditIfNotExists"
|
||||
|
||||
# =========================================================================
|
||||
# pgaudit.log value has specific categories → compliant
|
||||
# =========================================================================
|
||||
|
||||
- note: compliant_pgaudit_specific
|
||||
resource:
|
||||
type: "Microsoft.DBforPostgreSQL/flexibleServers"
|
||||
name: "pg-audit-specific"
|
||||
properties: {}
|
||||
host_await:
|
||||
- response:
|
||||
properties:
|
||||
value: "read,write,ddl"
|
||||
want_undefined: true
|
||||
|
||||
# =========================================================================
|
||||
# Wrong type → pass
|
||||
# =========================================================================
|
||||
|
||||
- note: pass_wrong_type
|
||||
resource:
|
||||
type: "Microsoft.Sql/servers"
|
||||
name: "not-pg"
|
||||
properties: {}
|
||||
want_undefined: true
|
||||
240
tests/azure_policy/cases/e2e_portal_dashboard_deny.yaml
Normal file
240
tests/azure_policy/cases/e2e_portal_dashboard_deny.yaml
Normal file
@@ -0,0 +1,240 @@
|
||||
# Copyright (c) Microsoft Corporation.
|
||||
# Licensed under the MIT License.
|
||||
|
||||
# E2E Test: Portal/SharedDashboardInlineContent_Deny
|
||||
# Real Azure Policy: "Shared dashboards should not have markdown tiles with inline content"
|
||||
# Source: regolator/policyDefinitions/Portal/SharedDashboardInlineContent_Deny.json
|
||||
#
|
||||
# Features exercised:
|
||||
# - Doubly-nested wildcard array: lenses[*].parts[*]
|
||||
# - requestContext().apiVersion with greaterOrEquals version comparison
|
||||
# - count with allOf+anyOf where clause
|
||||
# - Very long alias names (Extension-HubsExtension-PartType-MarkdownPart...)
|
||||
# - exists "false" inside count where
|
||||
# - anyOf at top level selects old-API OR inline-content branches
|
||||
|
||||
aliases: test_aliases.json
|
||||
|
||||
policy_definition: |
|
||||
{
|
||||
"properties": {
|
||||
"displayName": "Shared dashboards should not have markdown tiles with inline content",
|
||||
"policyType": "BuiltIn",
|
||||
"mode": "Indexed",
|
||||
"parameters": {
|
||||
"effect": {
|
||||
"type": "string",
|
||||
"defaultValue": "Audit",
|
||||
"allowedValues": ["Audit", "Deny", "Disabled"]
|
||||
}
|
||||
},
|
||||
"policyRule": {
|
||||
"if": {
|
||||
"allOf": [
|
||||
{
|
||||
"field": "type",
|
||||
"equals": "Microsoft.Portal/dashboards"
|
||||
},
|
||||
{
|
||||
"anyof": [
|
||||
{
|
||||
"not": {
|
||||
"value": "[requestContext().apiVersion]",
|
||||
"greaterOrEquals": "2020-09-01-alpha"
|
||||
}
|
||||
},
|
||||
{
|
||||
"count": {
|
||||
"field": "Microsoft.Portal/dashboards/lenses[*].parts[*]",
|
||||
"where": {
|
||||
"allOf": [
|
||||
{
|
||||
"field": "Microsoft.Portal/dashboards/lenses[*].parts[*].metadata.type",
|
||||
"equals": "Extension/HubsExtension/PartType/MarkdownPart"
|
||||
},
|
||||
{
|
||||
"anyOf": [
|
||||
{
|
||||
"field": "Microsoft.Portal/dashboards/lenses[*].parts[*].metadata.Extension-HubsExtension-PartType-MarkdownPart.settings.content.settings.markdownUri",
|
||||
"exists": "false"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.Portal/dashboards/lenses[*].parts[*].metadata.Extension-HubsExtension-PartType-MarkdownPart.settings.content.settings.markdownSource",
|
||||
"exists": "false"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.Portal/dashboards/lenses[*].parts[*].metadata.Extension-HubsExtension-PartType-MarkdownPart.settings.content.settings.markdownSource",
|
||||
"equals": "1"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"greater": 0
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"then": {
|
||||
"effect": "[parameters('effect')]"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cases:
|
||||
# =========================================================================
|
||||
# Old API version (< 2020-09-01-alpha) → always audit
|
||||
# =========================================================================
|
||||
|
||||
- note: audit_old_api_version
|
||||
resource:
|
||||
type: "Microsoft.Portal/dashboards"
|
||||
name: "dash-old-api"
|
||||
properties:
|
||||
lenses: []
|
||||
api_version: "2019-01-01-preview"
|
||||
want_effect: "Audit"
|
||||
|
||||
# =========================================================================
|
||||
# New API, markdown part with inline content (markdownSource = "1") → audit
|
||||
# =========================================================================
|
||||
|
||||
- note: audit_markdown_inline_content
|
||||
resource:
|
||||
type: "Microsoft.Portal/dashboards"
|
||||
name: "dash-inline"
|
||||
properties:
|
||||
lenses:
|
||||
- parts:
|
||||
- metadata:
|
||||
type: "Extension/HubsExtension/PartType/MarkdownPart"
|
||||
Extension-HubsExtension-PartType-MarkdownPart:
|
||||
settings:
|
||||
content:
|
||||
settings:
|
||||
markdownUri: "https://example.com/content.md"
|
||||
markdownSource: "1"
|
||||
api_version: "2022-12-01-preview"
|
||||
want_effect: "Audit"
|
||||
|
||||
# =========================================================================
|
||||
# New API, markdown part missing markdownUri → audit
|
||||
# =========================================================================
|
||||
|
||||
- note: audit_markdown_missing_uri
|
||||
resource:
|
||||
type: "Microsoft.Portal/dashboards"
|
||||
name: "dash-no-uri"
|
||||
properties:
|
||||
lenses:
|
||||
- parts:
|
||||
- metadata:
|
||||
type: "Extension/HubsExtension/PartType/MarkdownPart"
|
||||
Extension-HubsExtension-PartType-MarkdownPart:
|
||||
settings:
|
||||
content:
|
||||
settings:
|
||||
markdownSource: "2"
|
||||
api_version: "2022-12-01-preview"
|
||||
want_effect: "Audit"
|
||||
|
||||
# =========================================================================
|
||||
# New API, markdown part missing markdownSource → audit
|
||||
# =========================================================================
|
||||
|
||||
- note: audit_markdown_missing_source
|
||||
resource:
|
||||
type: "Microsoft.Portal/dashboards"
|
||||
name: "dash-no-source"
|
||||
properties:
|
||||
lenses:
|
||||
- parts:
|
||||
- metadata:
|
||||
type: "Extension/HubsExtension/PartType/MarkdownPart"
|
||||
Extension-HubsExtension-PartType-MarkdownPart:
|
||||
settings:
|
||||
content:
|
||||
settings:
|
||||
markdownUri: "https://example.com/content.md"
|
||||
api_version: "2022-12-01-preview"
|
||||
want_effect: "Audit"
|
||||
|
||||
# =========================================================================
|
||||
# New API, markdown part with external source (markdownSource != "1") → pass
|
||||
# =========================================================================
|
||||
|
||||
- note: pass_markdown_external_source
|
||||
resource:
|
||||
type: "Microsoft.Portal/dashboards"
|
||||
name: "dash-external"
|
||||
properties:
|
||||
lenses:
|
||||
- parts:
|
||||
- metadata:
|
||||
type: "Extension/HubsExtension/PartType/MarkdownPart"
|
||||
Extension-HubsExtension-PartType-MarkdownPart:
|
||||
settings:
|
||||
content:
|
||||
settings:
|
||||
markdownUri: "https://example.com/content.md"
|
||||
markdownSource: "2"
|
||||
api_version: "2022-12-01-preview"
|
||||
want_undefined: true
|
||||
|
||||
# =========================================================================
|
||||
# New API, non-markdown parts only → pass
|
||||
# =========================================================================
|
||||
|
||||
- note: pass_no_markdown_parts
|
||||
resource:
|
||||
type: "Microsoft.Portal/dashboards"
|
||||
name: "dash-charts"
|
||||
properties:
|
||||
lenses:
|
||||
- parts:
|
||||
- metadata:
|
||||
type: "Extension/HubsExtension/PartType/MonitorChartPart"
|
||||
- metadata:
|
||||
type: "Extension/HubsExtension/PartType/VideoPart"
|
||||
api_version: "2022-12-01-preview"
|
||||
want_undefined: true
|
||||
|
||||
# =========================================================================
|
||||
# New API, multiple lenses with mixed parts → audit if any inline
|
||||
# =========================================================================
|
||||
|
||||
- note: audit_multi_lens_one_inline
|
||||
resource:
|
||||
type: "Microsoft.Portal/dashboards"
|
||||
name: "dash-multi"
|
||||
properties:
|
||||
lenses:
|
||||
- parts:
|
||||
- metadata:
|
||||
type: "Extension/HubsExtension/PartType/MonitorChartPart"
|
||||
- parts:
|
||||
- metadata:
|
||||
type: "Extension/HubsExtension/PartType/MarkdownPart"
|
||||
Extension-HubsExtension-PartType-MarkdownPart:
|
||||
settings:
|
||||
content:
|
||||
settings:
|
||||
markdownSource: "1"
|
||||
markdownUri: "https://example.com/a.md"
|
||||
api_version: "2022-12-01-preview"
|
||||
want_effect: "Audit"
|
||||
|
||||
# =========================================================================
|
||||
# Wrong type
|
||||
# =========================================================================
|
||||
|
||||
- note: pass_wrong_type
|
||||
resource:
|
||||
type: "Microsoft.Compute/virtualMachines"
|
||||
name: "not-dashboard"
|
||||
properties: {}
|
||||
api_version: "2019-01-01"
|
||||
want_undefined: true
|
||||
116
tests/azure_policy/cases/e2e_servicebus_diagnostic_logs.yaml
Normal file
116
tests/azure_policy/cases/e2e_servicebus_diagnostic_logs.yaml
Normal file
@@ -0,0 +1,116 @@
|
||||
# Copyright (c) Microsoft Corporation.
|
||||
# Licensed under the MIT License.
|
||||
|
||||
# E2E Test: Service Bus/AuditDiagnosticLog_Audit
|
||||
# Features: AuditIfNotExists with inline existenceCondition evaluation,
|
||||
# including count-with-where and ARM template expression padLeft().
|
||||
#
|
||||
# NOTE: Without an alias catalog, fully-qualified field paths like
|
||||
# "Microsoft.Insights/diagnosticSettings/logs[*]..." resolve as raw
|
||||
# object keys. The test response structure mirrors this resolution.
|
||||
|
||||
policy_definition: |
|
||||
{
|
||||
"properties": {
|
||||
"displayName": "Resource logs in Service Bus should be enabled",
|
||||
"policyType": "BuiltIn",
|
||||
"mode": "Indexed",
|
||||
"parameters": {
|
||||
"effect": {
|
||||
"type": "string",
|
||||
"defaultValue": "AuditIfNotExists",
|
||||
"allowedValues": ["AuditIfNotExists", "Disabled"]
|
||||
},
|
||||
"requiredRetentionDays": {
|
||||
"type": "String",
|
||||
"defaultValue": "365"
|
||||
}
|
||||
},
|
||||
"policyRule": {
|
||||
"if": {
|
||||
"field": "type",
|
||||
"equals": "Microsoft.ServiceBus/namespaces"
|
||||
},
|
||||
"then": {
|
||||
"effect": "[parameters('effect')]",
|
||||
"details": {
|
||||
"type": "Microsoft.Insights/diagnosticSettings",
|
||||
"existenceCondition": {
|
||||
"count": {
|
||||
"field": "Microsoft.Insights/diagnosticSettings/logs[*]",
|
||||
"where": {
|
||||
"anyOf": [
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"field": "Microsoft.Insights/diagnosticSettings/logs[*].retentionPolicy.enabled",
|
||||
"equals": "true"
|
||||
},
|
||||
{
|
||||
"anyOf": [
|
||||
{
|
||||
"field": "Microsoft.Insights/diagnosticSettings/logs[*].retentionPolicy.days",
|
||||
"equals": "0"
|
||||
},
|
||||
{
|
||||
"value": "[padLeft(current('Microsoft.Insights/diagnosticSettings/logs[*].retentionPolicy.days'), 3, '0')]",
|
||||
"greaterOrEquals": "[padLeft(parameters('requiredRetentionDays'), 3, '0')]"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"greaterOrEquals": 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cases:
|
||||
# Related resource not found → non-compliant
|
||||
- note: non_compliant_resource_not_found
|
||||
resource:
|
||||
type: "Microsoft.ServiceBus/namespaces"
|
||||
name: "sb-a"
|
||||
properties: {}
|
||||
host_await:
|
||||
- key:
|
||||
operation: "lookup_related_resources"
|
||||
type: "Microsoft.Insights/diagnosticSettings"
|
||||
response: null
|
||||
want_effect: "AuditIfNotExists"
|
||||
|
||||
# Related resource found with compliant diagnostic log → compliant
|
||||
- note: compliant_resource_found_and_condition_passes
|
||||
resource:
|
||||
type: "Microsoft.ServiceBus/namespaces"
|
||||
name: "sb-b"
|
||||
properties: {}
|
||||
host_await:
|
||||
- response:
|
||||
Microsoft:
|
||||
"Insights/diagnosticSettings/logs":
|
||||
- retentionPolicy:
|
||||
enabled: "true"
|
||||
days: "365"
|
||||
want_undefined: true
|
||||
|
||||
# Related resource found but retention too short → non-compliant
|
||||
- note: non_compliant_retention_too_short
|
||||
resource:
|
||||
type: "Microsoft.ServiceBus/namespaces"
|
||||
name: "sb-c"
|
||||
properties: {}
|
||||
host_await:
|
||||
- response:
|
||||
Microsoft:
|
||||
"Insights/diagnosticSettings/logs":
|
||||
- retentionPolicy:
|
||||
enabled: "true"
|
||||
days: "10"
|
||||
want_effect: "AuditIfNotExists"
|
||||
204
tests/azure_policy/cases/e2e_shared_dashboard_deny.yaml
Normal file
204
tests/azure_policy/cases/e2e_shared_dashboard_deny.yaml
Normal file
@@ -0,0 +1,204 @@
|
||||
# Copyright (c) Microsoft Corporation.
|
||||
# Licensed under the MIT License.
|
||||
|
||||
# E2E Test: Portal/SharedDashboardInlineContent_Deny
|
||||
# Real Azure Policy: "Shared dashboards should not have markdown tiles with inline content"
|
||||
# Source: regolator/policyDefinitions/Portal/SharedDashboardInlineContent_Deny.json
|
||||
#
|
||||
# Features exercised:
|
||||
# - Doubly-nested wildcard: lenses[*].parts[*]
|
||||
# - Field count with where clause on deeply-nested metadata paths
|
||||
# - requestContext().apiVersion guard with NOT (double negation pattern)
|
||||
# - "anyof" (lowercase 'o') variant
|
||||
|
||||
aliases: test_aliases.json
|
||||
|
||||
policy_definition: |
|
||||
{
|
||||
"properties": {
|
||||
"displayName": "Shared dashboards should not have markdown tiles with inline content",
|
||||
"policyType": "BuiltIn",
|
||||
"mode": "Indexed",
|
||||
"parameters": {
|
||||
"effect": {
|
||||
"type": "string",
|
||||
"defaultValue": "Audit",
|
||||
"allowedValues": ["Audit", "Deny", "Disabled"]
|
||||
}
|
||||
},
|
||||
"policyRule": {
|
||||
"if": {
|
||||
"allOf": [
|
||||
{
|
||||
"field": "type",
|
||||
"equals": "Microsoft.Portal/dashboards"
|
||||
},
|
||||
{
|
||||
"anyof": [
|
||||
{
|
||||
"not": {
|
||||
"value": "[requestContext().apiVersion]",
|
||||
"greaterOrEquals": "2020-09-01-alpha"
|
||||
}
|
||||
},
|
||||
{
|
||||
"count": {
|
||||
"field": "Microsoft.Portal/dashboards/lenses[*].parts[*]",
|
||||
"where": {
|
||||
"allOf": [
|
||||
{
|
||||
"field": "Microsoft.Portal/dashboards/lenses[*].parts[*].metadata.type",
|
||||
"equals": "Extension/HubsExtension/PartType/MarkdownPart"
|
||||
},
|
||||
{
|
||||
"anyOf": [
|
||||
{
|
||||
"field": "Microsoft.Portal/dashboards/lenses[*].parts[*].metadata.Extension-HubsExtension-PartType-MarkdownPart.settings.content.settings.markdownUri",
|
||||
"exists": "false"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.Portal/dashboards/lenses[*].parts[*].metadata.Extension-HubsExtension-PartType-MarkdownPart.settings.content.settings.markdownSource",
|
||||
"exists": "false"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.Portal/dashboards/lenses[*].parts[*].metadata.Extension-HubsExtension-PartType-MarkdownPart.settings.content.settings.markdownSource",
|
||||
"equals": "1"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"greater": 0
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"then": {
|
||||
"effect": "[parameters('effect')]"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cases:
|
||||
# =========================================================================
|
||||
# Old API version → triggers (not greaterOrEquals "2020-09-01-alpha")
|
||||
# =========================================================================
|
||||
|
||||
- note: audit_old_api_version
|
||||
resource:
|
||||
type: "Microsoft.Portal/dashboards"
|
||||
name: "old-api-dashboard"
|
||||
properties:
|
||||
lenses: []
|
||||
request_context:
|
||||
apiVersion: "2019-01-01"
|
||||
want_effect: "Audit"
|
||||
|
||||
# =========================================================================
|
||||
# New API version, markdown part with inline content (no markdownUri)
|
||||
# =========================================================================
|
||||
|
||||
- note: audit_inline_markdown_no_uri
|
||||
resource:
|
||||
type: "Microsoft.Portal/dashboards"
|
||||
name: "inline-md"
|
||||
properties:
|
||||
lenses:
|
||||
- parts:
|
||||
- metadata:
|
||||
type: "Extension/HubsExtension/PartType/MarkdownPart"
|
||||
Extension-HubsExtension-PartType-MarkdownPart:
|
||||
settings:
|
||||
content:
|
||||
settings:
|
||||
markdownSource: "1"
|
||||
request_context:
|
||||
apiVersion: "2022-12-01"
|
||||
want_effect: "Audit"
|
||||
|
||||
# =========================================================================
|
||||
# New API version, markdown part with markdownUri set → compliant
|
||||
# =========================================================================
|
||||
|
||||
- note: pass_markdown_with_uri
|
||||
resource:
|
||||
type: "Microsoft.Portal/dashboards"
|
||||
name: "external-md"
|
||||
properties:
|
||||
lenses:
|
||||
- parts:
|
||||
- metadata:
|
||||
type: "Extension/HubsExtension/PartType/MarkdownPart"
|
||||
Extension-HubsExtension-PartType-MarkdownPart:
|
||||
settings:
|
||||
content:
|
||||
settings:
|
||||
markdownUri: "https://raw.githubusercontent.com/org/repo/main/README.md"
|
||||
markdownSource: "url"
|
||||
request_context:
|
||||
apiVersion: "2022-12-01"
|
||||
want_undefined: true
|
||||
|
||||
# =========================================================================
|
||||
# Non-markdown part → compliant
|
||||
# =========================================================================
|
||||
|
||||
- note: pass_non_markdown_part
|
||||
resource:
|
||||
type: "Microsoft.Portal/dashboards"
|
||||
name: "chart-dashboard"
|
||||
properties:
|
||||
lenses:
|
||||
- parts:
|
||||
- metadata:
|
||||
type: "Extension/HubsExtension/PartType/MonitorChartPart"
|
||||
request_context:
|
||||
apiVersion: "2022-12-01"
|
||||
want_undefined: true
|
||||
|
||||
# =========================================================================
|
||||
# Multiple lenses/parts, mixed: one inline, one external → triggers
|
||||
# =========================================================================
|
||||
|
||||
- note: audit_mixed_parts_one_inline
|
||||
resource:
|
||||
type: "Microsoft.Portal/dashboards"
|
||||
name: "mixed-dashboard"
|
||||
properties:
|
||||
lenses:
|
||||
- parts:
|
||||
- metadata:
|
||||
type: "Extension/HubsExtension/PartType/MonitorChartPart"
|
||||
- metadata:
|
||||
type: "Extension/HubsExtension/PartType/MarkdownPart"
|
||||
Extension-HubsExtension-PartType-MarkdownPart:
|
||||
settings:
|
||||
content:
|
||||
settings:
|
||||
markdownSource: "1"
|
||||
- parts:
|
||||
- metadata:
|
||||
type: "Extension/HubsExtension/PartType/MarkdownPart"
|
||||
Extension-HubsExtension-PartType-MarkdownPart:
|
||||
settings:
|
||||
content:
|
||||
settings:
|
||||
markdownUri: "https://example.com/md.md"
|
||||
markdownSource: "url"
|
||||
request_context:
|
||||
apiVersion: "2022-12-01"
|
||||
want_effect: "Audit"
|
||||
|
||||
# =========================================================================
|
||||
# Wrong type → skip
|
||||
# =========================================================================
|
||||
|
||||
- note: skip_wrong_type
|
||||
resource:
|
||||
type: "Microsoft.Storage/storageAccounts"
|
||||
name: "storage"
|
||||
properties: {}
|
||||
want_undefined: true
|
||||
183
tests/azure_policy/cases/e2e_signalr_public_network_modify.yaml
Normal file
183
tests/azure_policy/cases/e2e_signalr_public_network_modify.yaml
Normal file
@@ -0,0 +1,183 @@
|
||||
# Copyright (c) Microsoft Corporation.
|
||||
# Licensed under the MIT License.
|
||||
|
||||
# E2E Test: SignalR/PublicNetworkAccessDisabled_Modify
|
||||
# Real Azure Policy: "Modify Azure SignalR Service resources to disable public network access"
|
||||
# Source: regolator/policyDefinitions/SignalR/PublicNetworkAccessDisabled_Modify.json
|
||||
#
|
||||
# Features exercised:
|
||||
# - Multi-modify (3 operations, one conditional)
|
||||
# - Conditional modify gated on requestContext().apiVersion
|
||||
# - conflictEffect: Audit
|
||||
# - Bare field count (no where clause)
|
||||
# - exists false
|
||||
|
||||
aliases: test_aliases.json
|
||||
|
||||
policy_definition: |
|
||||
{
|
||||
"properties": {
|
||||
"displayName": "Modify Azure SignalR Service resources to disable public network access",
|
||||
"policyType": "BuiltIn",
|
||||
"mode": "Indexed",
|
||||
"parameters": {
|
||||
"effect": {
|
||||
"type": "String",
|
||||
"allowedValues": ["Modify", "Disabled"],
|
||||
"defaultValue": "Modify"
|
||||
}
|
||||
},
|
||||
"policyRule": {
|
||||
"if": {
|
||||
"allOf": [
|
||||
{
|
||||
"field": "type",
|
||||
"equals": "Microsoft.SignalRService/SignalR"
|
||||
},
|
||||
{
|
||||
"anyOf": [
|
||||
{
|
||||
"field": "Microsoft.SignalRService/SignalR/networkACLs.defaultAction",
|
||||
"equals": "Allow"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.SignalRService/SignalR/networkACLs.publicNetwork.allow",
|
||||
"exists": false
|
||||
},
|
||||
{
|
||||
"count": {
|
||||
"field": "Microsoft.SignalRService/SignalR/networkACLs.publicNetwork.allow[*]"
|
||||
},
|
||||
"greater": 0
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.SignalRService/SignalR/publicNetworkAccess",
|
||||
"notEquals": "Disabled"
|
||||
}
|
||||
]
|
||||
},
|
||||
"then": {
|
||||
"effect": "[parameters('effect')]",
|
||||
"details": {
|
||||
"conflictEffect": "Audit",
|
||||
"roleDefinitionIds": [
|
||||
"/providers/Microsoft.Authorization/roleDefinitions/8cf5e20a-e4b2-4e9d-b3a1-5ceb692c2761"
|
||||
],
|
||||
"operations": [
|
||||
{
|
||||
"operation": "addOrReplace",
|
||||
"field": "Microsoft.SignalRService/SignalR/networkACLs.defaultAction",
|
||||
"value": "Deny"
|
||||
},
|
||||
{
|
||||
"operation": "addOrReplace",
|
||||
"field": "Microsoft.SignalRService/SignalR/networkACLs.publicNetwork.allow",
|
||||
"value": []
|
||||
},
|
||||
{
|
||||
"condition": "[greaterOrEquals(requestContext().apiVersion, '2021-06-01-preview')]",
|
||||
"operation": "addOrReplace",
|
||||
"field": "Microsoft.SignalRService/SignalR/publicNetworkAccess",
|
||||
"value": "Disabled"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cases:
|
||||
# =========================================================================
|
||||
# Modify — default action is Allow
|
||||
# =========================================================================
|
||||
|
||||
- note: modify_default_action_allow
|
||||
resource:
|
||||
type: "Microsoft.SignalRService/SignalR"
|
||||
name: "signalr-open"
|
||||
properties:
|
||||
networkACLs:
|
||||
defaultAction: "Allow"
|
||||
publicNetwork:
|
||||
allow:
|
||||
- "ServerConnection"
|
||||
publicNetworkAccess: "Enabled"
|
||||
want_effect: "Modify"
|
||||
|
||||
# =========================================================================
|
||||
# Modify — publicNetwork.allow does not exist
|
||||
# =========================================================================
|
||||
|
||||
- note: modify_public_allow_not_exists
|
||||
resource:
|
||||
type: "Microsoft.SignalRService/SignalR"
|
||||
name: "signalr-no-allow"
|
||||
properties:
|
||||
networkACLs:
|
||||
defaultAction: "Deny"
|
||||
publicNetworkAccess: "Enabled"
|
||||
want_effect: "Modify"
|
||||
|
||||
# =========================================================================
|
||||
# Modify — publicNetwork.allow has items (count > 0)
|
||||
# =========================================================================
|
||||
|
||||
- note: modify_public_allow_has_items
|
||||
resource:
|
||||
type: "Microsoft.SignalRService/SignalR"
|
||||
name: "signalr-with-allow"
|
||||
properties:
|
||||
networkACLs:
|
||||
defaultAction: "Deny"
|
||||
publicNetwork:
|
||||
allow:
|
||||
- "ClientConnection"
|
||||
- "ServerConnection"
|
||||
publicNetworkAccess: "Enabled"
|
||||
want_effect: "Modify"
|
||||
|
||||
# =========================================================================
|
||||
# Compliant — already disabled
|
||||
# =========================================================================
|
||||
|
||||
- note: pass_already_disabled
|
||||
resource:
|
||||
type: "Microsoft.SignalRService/SignalR"
|
||||
name: "signalr-locked"
|
||||
properties:
|
||||
networkACLs:
|
||||
defaultAction: "Deny"
|
||||
publicNetwork:
|
||||
allow: []
|
||||
publicNetworkAccess: "Disabled"
|
||||
want_undefined: true
|
||||
|
||||
# =========================================================================
|
||||
# Compliant — defaultAction=Deny, no allow field, already disabled
|
||||
# =========================================================================
|
||||
|
||||
- note: pass_deny_no_allow_disabled
|
||||
resource:
|
||||
type: "Microsoft.SignalRService/SignalR"
|
||||
name: "signalr-minimal"
|
||||
properties:
|
||||
networkACLs:
|
||||
defaultAction: "Deny"
|
||||
publicNetwork:
|
||||
allow: []
|
||||
publicNetworkAccess: "Disabled"
|
||||
want_undefined: true
|
||||
|
||||
# =========================================================================
|
||||
# Wrong type → skip
|
||||
# =========================================================================
|
||||
|
||||
- note: skip_wrong_type
|
||||
resource:
|
||||
type: "Microsoft.SignalRService/WebPubSub"
|
||||
name: "webpubsub"
|
||||
properties: {}
|
||||
want_undefined: true
|
||||
183
tests/azure_policy/cases/e2e_sql_audit_actions_aine.yaml
Normal file
183
tests/azure_policy/cases/e2e_sql_audit_actions_aine.yaml
Normal file
@@ -0,0 +1,183 @@
|
||||
# Copyright (c) Microsoft Corporation.
|
||||
# Licensed under the MIT License.
|
||||
|
||||
# E2E Test: SQL/SqlServerAuditing_ActionsAndGroups_Audit
|
||||
# Real Azure Policy: "SQL Auditing settings should have Action-Groups
|
||||
# configured to capture critical activities"
|
||||
# Source: regolator/policyDefinitions/SQL/SqlServerAuditing_ActionsAndGroups_Audit.json
|
||||
#
|
||||
# Features exercised:
|
||||
# - AuditIfNotExists with existenceCondition
|
||||
# - Triple double-negation: not { field[*] notEquals X } — asserts X is
|
||||
# present in the array (aka "all elements are not-not-equal to X")
|
||||
# - Wildcard array alias: auditActionsAndGroups[*]
|
||||
# - host_await for cross-resource lookup
|
||||
|
||||
aliases: test_aliases.json
|
||||
|
||||
policy_definition: |
|
||||
{
|
||||
"properties": {
|
||||
"displayName": "SQL Auditing settings should have Action-Groups configured to capture critical activities",
|
||||
"policyType": "BuiltIn",
|
||||
"mode": "Indexed",
|
||||
"parameters": {
|
||||
"effect": {
|
||||
"type": "string",
|
||||
"defaultValue": "AuditIfNotExists",
|
||||
"allowedValues": ["AuditIfNotExists", "Disabled"]
|
||||
}
|
||||
},
|
||||
"policyRule": {
|
||||
"if": {
|
||||
"field": "type",
|
||||
"equals": "Microsoft.Sql/servers"
|
||||
},
|
||||
"then": {
|
||||
"effect": "[parameters('effect')]",
|
||||
"details": {
|
||||
"type": "Microsoft.Sql/servers/auditingSettings",
|
||||
"name": "default",
|
||||
"existenceCondition": {
|
||||
"allOf": [
|
||||
{
|
||||
"not": {
|
||||
"field": "Microsoft.Sql/servers/auditingSettings/auditActionsAndGroups[*]",
|
||||
"notEquals": "SUCCESSFUL_DATABASE_AUTHENTICATION_GROUP"
|
||||
}
|
||||
},
|
||||
{
|
||||
"not": {
|
||||
"field": "Microsoft.Sql/servers/auditingSettings/auditActionsAndGroups[*]",
|
||||
"notEquals": "FAILED_DATABASE_AUTHENTICATION_GROUP"
|
||||
}
|
||||
},
|
||||
{
|
||||
"not": {
|
||||
"field": "Microsoft.Sql/servers/auditingSettings/auditActionsAndGroups[*]",
|
||||
"notEquals": "BATCH_COMPLETED_GROUP"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cases:
|
||||
# =========================================================================
|
||||
# Related resource not found → AuditIfNotExists
|
||||
# =========================================================================
|
||||
|
||||
- note: aine_no_related_resource
|
||||
resource:
|
||||
type: "Microsoft.Sql/servers"
|
||||
name: "sql-no-audit"
|
||||
properties: {}
|
||||
host_await:
|
||||
- key:
|
||||
operation: "lookup_related_resources"
|
||||
type: "Microsoft.Sql/servers/auditingSettings"
|
||||
name: "default"
|
||||
response: null
|
||||
want_effect: "AuditIfNotExists"
|
||||
|
||||
# =========================================================================
|
||||
# All three groups present → compliant (undefined)
|
||||
# =========================================================================
|
||||
|
||||
- note: compliant_all_three_groups
|
||||
resource:
|
||||
type: "Microsoft.Sql/servers"
|
||||
name: "sql-complete"
|
||||
properties: {}
|
||||
host_await:
|
||||
- response:
|
||||
properties:
|
||||
auditactionsandgroups:
|
||||
- "SUCCESSFUL_DATABASE_AUTHENTICATION_GROUP"
|
||||
- "FAILED_DATABASE_AUTHENTICATION_GROUP"
|
||||
- "BATCH_COMPLETED_GROUP"
|
||||
want_undefined: true
|
||||
|
||||
# =========================================================================
|
||||
# All three present among extras → still compliant
|
||||
# =========================================================================
|
||||
|
||||
- note: compliant_extras_present
|
||||
resource:
|
||||
type: "Microsoft.Sql/servers"
|
||||
name: "sql-extras"
|
||||
properties: {}
|
||||
host_await:
|
||||
- response:
|
||||
properties:
|
||||
auditactionsandgroups:
|
||||
- "SUCCESSFUL_DATABASE_AUTHENTICATION_GROUP"
|
||||
- "FAILED_DATABASE_AUTHENTICATION_GROUP"
|
||||
- "BATCH_COMPLETED_GROUP"
|
||||
- "APPLICATION_ROLE_CHANGE_PASSWORD_GROUP"
|
||||
- "DATABASE_OBJECT_ACCESS_GROUP"
|
||||
want_undefined: true
|
||||
|
||||
# =========================================================================
|
||||
# Missing BATCH_COMPLETED_GROUP → non-compliant
|
||||
# =========================================================================
|
||||
|
||||
- note: aine_missing_batch_completed
|
||||
resource:
|
||||
type: "Microsoft.Sql/servers"
|
||||
name: "sql-missing-batch"
|
||||
properties: {}
|
||||
host_await:
|
||||
- response:
|
||||
properties:
|
||||
auditactionsandgroups:
|
||||
- "SUCCESSFUL_DATABASE_AUTHENTICATION_GROUP"
|
||||
- "FAILED_DATABASE_AUTHENTICATION_GROUP"
|
||||
want_effect: "AuditIfNotExists"
|
||||
|
||||
# =========================================================================
|
||||
# Missing SUCCESSFUL_DATABASE_AUTHENTICATION_GROUP → non-compliant
|
||||
# =========================================================================
|
||||
|
||||
- note: aine_missing_successful_auth
|
||||
resource:
|
||||
type: "Microsoft.Sql/servers"
|
||||
name: "sql-missing-success"
|
||||
properties: {}
|
||||
host_await:
|
||||
- response:
|
||||
properties:
|
||||
auditactionsandgroups:
|
||||
- "FAILED_DATABASE_AUTHENTICATION_GROUP"
|
||||
- "BATCH_COMPLETED_GROUP"
|
||||
want_effect: "AuditIfNotExists"
|
||||
|
||||
# =========================================================================
|
||||
# Empty array → all three missing → non-compliant
|
||||
# =========================================================================
|
||||
|
||||
- note: aine_empty_groups
|
||||
resource:
|
||||
type: "Microsoft.Sql/servers"
|
||||
name: "sql-empty-groups"
|
||||
properties: {}
|
||||
host_await:
|
||||
- response:
|
||||
properties:
|
||||
auditactionsandgroups: []
|
||||
want_effect: "AuditIfNotExists"
|
||||
|
||||
# =========================================================================
|
||||
# Wrong type → pass (if-condition fails)
|
||||
# =========================================================================
|
||||
|
||||
- note: pass_wrong_type
|
||||
resource:
|
||||
type: "Microsoft.Storage/storageAccounts"
|
||||
name: "not-sql"
|
||||
properties: {}
|
||||
want_undefined: true
|
||||
104
tests/azure_policy/cases/e2e_sql_server_auditing.yaml
Normal file
104
tests/azure_policy/cases/e2e_sql_server_auditing.yaml
Normal file
@@ -0,0 +1,104 @@
|
||||
# Copyright (c) Microsoft Corporation.
|
||||
# Licensed under the MIT License.
|
||||
|
||||
# E2E Test: SQL/SqlServerAuditing_Audit
|
||||
# Features: AuditIfNotExists with inline existenceCondition evaluation.
|
||||
# The host returns the related resource (or null if not found), and the
|
||||
# compiler evaluates the existenceCondition against it.
|
||||
#
|
||||
# NOTE: Without an alias catalog, fully-qualified field paths like
|
||||
# "Microsoft.Sql/auditingSettings.state" resolve as raw object keys
|
||||
# ("Microsoft" → "Sql/auditingSettings" → "state"). The test response
|
||||
# structure mirrors this resolution.
|
||||
|
||||
policy_definition: |
|
||||
{
|
||||
"properties": {
|
||||
"displayName": "Auditing on SQL server should be enabled",
|
||||
"policyType": "BuiltIn",
|
||||
"mode": "Indexed",
|
||||
"parameters": {
|
||||
"effect": {
|
||||
"type": "string",
|
||||
"defaultValue": "AuditIfNotExists",
|
||||
"allowedValues": ["AuditIfNotExists", "Disabled"]
|
||||
},
|
||||
"setting": {
|
||||
"type": "String",
|
||||
"defaultValue": "enabled",
|
||||
"allowedValues": ["enabled", "disabled"]
|
||||
}
|
||||
},
|
||||
"policyRule": {
|
||||
"if": {
|
||||
"allOf": [
|
||||
{"field": "type", "equals": "Microsoft.Sql/servers"},
|
||||
{"field": "kind", "notContains": "analytics"}
|
||||
]
|
||||
},
|
||||
"then": {
|
||||
"effect": "[parameters('effect')]",
|
||||
"details": {
|
||||
"type": "Microsoft.Sql/servers/auditingSettings",
|
||||
"name": "default",
|
||||
"existenceCondition": {
|
||||
"field": "Microsoft.Sql/auditingSettings.state",
|
||||
"equals": "[parameters('setting')]"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cases:
|
||||
# Related resource not found → non-compliant
|
||||
- note: non_compliant_resource_not_found
|
||||
resource:
|
||||
type: "Microsoft.Sql/servers"
|
||||
kind: "v12.0"
|
||||
name: "sql-a"
|
||||
properties: {}
|
||||
host_await:
|
||||
- key:
|
||||
operation: "lookup_related_resources"
|
||||
type: "Microsoft.Sql/servers/auditingSettings"
|
||||
name: "default"
|
||||
response: null
|
||||
want_effect: "AuditIfNotExists"
|
||||
|
||||
# Related resource found and existenceCondition matches → compliant
|
||||
- note: compliant_resource_found_and_condition_passes
|
||||
resource:
|
||||
type: "Microsoft.Sql/servers"
|
||||
kind: "v12.0"
|
||||
name: "sql-b"
|
||||
properties: {}
|
||||
host_await:
|
||||
- response:
|
||||
Microsoft:
|
||||
"Sql/auditingSettings":
|
||||
state: "enabled"
|
||||
want_undefined: true
|
||||
|
||||
# Related resource found but existenceCondition fails → non-compliant
|
||||
- note: non_compliant_condition_fails
|
||||
resource:
|
||||
type: "Microsoft.Sql/servers"
|
||||
kind: "v12.0"
|
||||
name: "sql-c"
|
||||
properties: {}
|
||||
host_await:
|
||||
- response:
|
||||
Microsoft:
|
||||
"Sql/auditingSettings":
|
||||
state: "disabled"
|
||||
want_effect: "AuditIfNotExists"
|
||||
|
||||
# Primary resource type doesn't match → if-condition false → compliant
|
||||
- note: pass_when_type_not_matching
|
||||
resource:
|
||||
type: "Microsoft.Storage/storageAccounts"
|
||||
name: "stg"
|
||||
properties: {}
|
||||
want_undefined: true
|
||||
1029
tests/azure_policy/cases/e2e_ssh_security_baseline_dine.yaml
Normal file
1029
tests/azure_policy/cases/e2e_ssh_security_baseline_dine.yaml
Normal file
File diff suppressed because it is too large
Load Diff
243
tests/azure_policy/cases/e2e_storage_bypass_modify.yaml
Normal file
243
tests/azure_policy/cases/e2e_storage_bypass_modify.yaml
Normal file
@@ -0,0 +1,243 @@
|
||||
# Copyright (c) Microsoft Corporation.
|
||||
# Licensed under the MIT License.
|
||||
|
||||
# E2E Test: VirtualEnclaves/StorageNetworkAccessBypassOnly_Modify
|
||||
# Real Azure Policy: "Configure Storage Accounts to restrict network access
|
||||
# through network ACL bypass configuration only"
|
||||
# Source: regolator/policyDefinitions/VirtualEnclaves/StorageNetworkAccessBypassOnly_Modify.json
|
||||
#
|
||||
# Features exercised:
|
||||
# - Three plain field counts (ipRules[*], resourceAccessRules[*], virtualNetworkRules[*])
|
||||
# - greaterOrEquals 1 count comparison
|
||||
# - Modify with 4 addOrReplace operations (no condition)
|
||||
# - parameterised bypass configuration
|
||||
# - notEquals on field
|
||||
|
||||
aliases: test_aliases.json
|
||||
|
||||
policy_definition: |
|
||||
{
|
||||
"properties": {
|
||||
"displayName": "Configure Storage Accounts to restrict network access through network ACL bypass configuration only",
|
||||
"policyType": "BuiltIn",
|
||||
"mode": "Indexed",
|
||||
"parameters": {
|
||||
"effect": {
|
||||
"type": "String",
|
||||
"allowedValues": ["Modify", "Disabled"],
|
||||
"defaultValue": "Modify"
|
||||
},
|
||||
"bypassConfiguration": {
|
||||
"type": "String",
|
||||
"allowedValues": [
|
||||
"None",
|
||||
"AzureServices",
|
||||
"Logging",
|
||||
"Metrics",
|
||||
"Logging, Metrics",
|
||||
"Logging, Metrics, AzureServices",
|
||||
"Logging, AzureServices",
|
||||
"Metrics, AzureServices"
|
||||
],
|
||||
"defaultValue": "AzureServices"
|
||||
}
|
||||
},
|
||||
"policyRule": {
|
||||
"if": {
|
||||
"allOf": [
|
||||
{
|
||||
"field": "type",
|
||||
"equals": "Microsoft.Storage/storageAccounts"
|
||||
},
|
||||
{
|
||||
"anyOf": [
|
||||
{
|
||||
"field": "Microsoft.Storage/storageAccounts/networkAcls.defaultAction",
|
||||
"notEquals": "Deny"
|
||||
},
|
||||
{
|
||||
"count": {
|
||||
"field": "Microsoft.Storage/storageAccounts/networkAcls.ipRules[*]"
|
||||
},
|
||||
"greaterOrEquals": 1
|
||||
},
|
||||
{
|
||||
"count": {
|
||||
"field": "Microsoft.Storage/storageAccounts/networkAcls.resourceAccessRules[*]"
|
||||
},
|
||||
"greaterOrEquals": 1
|
||||
},
|
||||
{
|
||||
"count": {
|
||||
"field": "Microsoft.Storage/storageAccounts/networkAcls.virtualNetworkRules[*]"
|
||||
},
|
||||
"greaterOrEquals": 1
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.Storage/storageAccounts/networkAcls.bypass",
|
||||
"notEquals": "[parameters('bypassConfiguration')]"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"then": {
|
||||
"effect": "[parameters('effect')]",
|
||||
"details": {
|
||||
"conflictEffect": "audit",
|
||||
"roleDefinitionIds": [
|
||||
"/providers/Microsoft.Authorization/roleDefinitions/17d1049b-9a84-46fb-8f53-869881c3d3ab"
|
||||
],
|
||||
"operations": [
|
||||
{
|
||||
"operation": "addOrReplace",
|
||||
"field": "Microsoft.Storage/storageAccounts/networkAcls.defaultAction",
|
||||
"value": "Deny"
|
||||
},
|
||||
{
|
||||
"operation": "addOrReplace",
|
||||
"field": "Microsoft.Storage/storageAccounts/networkAcls.ipRules",
|
||||
"value": []
|
||||
},
|
||||
{
|
||||
"operation": "addOrReplace",
|
||||
"field": "Microsoft.Storage/storageAccounts/networkAcls.virtualNetworkRules",
|
||||
"value": []
|
||||
},
|
||||
{
|
||||
"operation": "addOrReplace",
|
||||
"field": "Microsoft.Storage/storageAccounts/networkAcls.bypass",
|
||||
"value": "[parameters('bypassConfiguration')]"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cases:
|
||||
# =========================================================================
|
||||
# Fully compliant — defaultAction=Deny, no rules, bypass matches
|
||||
# =========================================================================
|
||||
|
||||
- note: pass_fully_compliant
|
||||
resource:
|
||||
type: "Microsoft.Storage/storageAccounts"
|
||||
name: "stg-compliant"
|
||||
properties:
|
||||
networkAcls:
|
||||
defaultAction: "Deny"
|
||||
ipRules: []
|
||||
resourceAccessRules: []
|
||||
virtualNetworkRules: []
|
||||
bypass: "AzureServices"
|
||||
want_undefined: true
|
||||
|
||||
# =========================================================================
|
||||
# defaultAction != Deny → Modify
|
||||
# =========================================================================
|
||||
|
||||
- note: modify_default_action_allow
|
||||
resource:
|
||||
type: "Microsoft.Storage/storageAccounts"
|
||||
name: "stg-allow"
|
||||
properties:
|
||||
networkAcls:
|
||||
defaultAction: "Allow"
|
||||
ipRules: []
|
||||
resourceAccessRules: []
|
||||
virtualNetworkRules: []
|
||||
bypass: "AzureServices"
|
||||
want_effect: "Modify"
|
||||
want_details:
|
||||
roleDefinitionIds:
|
||||
- "/providers/Microsoft.Authorization/roleDefinitions/17d1049b-9a84-46fb-8f53-869881c3d3ab"
|
||||
operations:
|
||||
- operation: "addOrReplace"
|
||||
field: "Microsoft.Storage/storageAccounts/networkAcls.defaultAction"
|
||||
value: "Deny"
|
||||
- operation: "addOrReplace"
|
||||
field: "Microsoft.Storage/storageAccounts/networkAcls.ipRules"
|
||||
value: []
|
||||
- operation: "addOrReplace"
|
||||
field: "Microsoft.Storage/storageAccounts/networkAcls.virtualNetworkRules"
|
||||
value: []
|
||||
- operation: "addOrReplace"
|
||||
field: "Microsoft.Storage/storageAccounts/networkAcls.bypass"
|
||||
value: "AzureServices"
|
||||
|
||||
# =========================================================================
|
||||
# Has IP rules → count >= 1 triggers Modify
|
||||
# =========================================================================
|
||||
|
||||
- note: modify_has_ip_rules
|
||||
resource:
|
||||
type: "Microsoft.Storage/storageAccounts"
|
||||
name: "stg-ip-rules"
|
||||
properties:
|
||||
networkAcls:
|
||||
defaultAction: "Deny"
|
||||
ipRules:
|
||||
- value: "10.0.0.1"
|
||||
action: "Allow"
|
||||
resourceAccessRules: []
|
||||
virtualNetworkRules: []
|
||||
bypass: "AzureServices"
|
||||
want_effect: "Modify"
|
||||
|
||||
# =========================================================================
|
||||
# Has vnet rules → count >= 1 triggers Modify
|
||||
# =========================================================================
|
||||
|
||||
- note: modify_has_vnet_rules
|
||||
resource:
|
||||
type: "Microsoft.Storage/storageAccounts"
|
||||
name: "stg-vnet-rules"
|
||||
properties:
|
||||
networkAcls:
|
||||
defaultAction: "Deny"
|
||||
ipRules: []
|
||||
resourceAccessRules: []
|
||||
virtualNetworkRules:
|
||||
- id: "/subscriptions/sub1/resourceGroups/rg1/providers/Microsoft.Network/virtualNetworks/vnet1/subnets/subnet1"
|
||||
bypass: "AzureServices"
|
||||
want_effect: "Modify"
|
||||
|
||||
# =========================================================================
|
||||
# bypass != parameter → Modify
|
||||
# =========================================================================
|
||||
|
||||
- note: modify_bypass_mismatch
|
||||
resource:
|
||||
type: "Microsoft.Storage/storageAccounts"
|
||||
name: "stg-bypass"
|
||||
properties:
|
||||
networkAcls:
|
||||
defaultAction: "Deny"
|
||||
ipRules: []
|
||||
resourceAccessRules: []
|
||||
virtualNetworkRules: []
|
||||
bypass: "None"
|
||||
parameters:
|
||||
bypassConfiguration: "AzureServices"
|
||||
want_effect: "Modify"
|
||||
|
||||
# =========================================================================
|
||||
# Custom bypass param, bypass matches → pass
|
||||
# =========================================================================
|
||||
|
||||
- note: pass_custom_bypass_matches
|
||||
resource:
|
||||
type: "Microsoft.Storage/storageAccounts"
|
||||
name: "stg-custom-bypass"
|
||||
properties:
|
||||
networkAcls:
|
||||
defaultAction: "Deny"
|
||||
ipRules: []
|
||||
resourceAccessRules: []
|
||||
virtualNetworkRules: []
|
||||
bypass: "Logging, Metrics"
|
||||
parameters:
|
||||
bypassConfiguration: "Logging, Metrics"
|
||||
want_undefined: true
|
||||
114
tests/azure_policy/cases/e2e_storage_ip_allowlist.yaml
Normal file
114
tests/azure_policy/cases/e2e_storage_ip_allowlist.yaml
Normal file
@@ -0,0 +1,114 @@
|
||||
# Copyright (c) Microsoft Corporation.
|
||||
# Licensed under the MIT License.
|
||||
|
||||
# E2E Test: Custom Storage IP allowlist
|
||||
# Features: count.field + where(allOf) with action gate + notIn(parameters()) +
|
||||
# parameter defaults from policy definition.
|
||||
|
||||
aliases: test_aliases.json
|
||||
|
||||
policy_definition: |
|
||||
{
|
||||
"properties": {
|
||||
"displayName": "Storage IP allowlist",
|
||||
"policyType": "Custom",
|
||||
"mode": "Indexed",
|
||||
"description": "Only allow IP rules that match an approved allowlist.",
|
||||
"metadata": {
|
||||
"version": "1.0.0",
|
||||
"category": "Network"
|
||||
},
|
||||
"version": "1.0.0",
|
||||
"parameters": {
|
||||
"allowedIps": {
|
||||
"type": "Array",
|
||||
"metadata": {
|
||||
"displayName": "Allowed IPs",
|
||||
"description": "IP addresses permitted in IP rules"
|
||||
},
|
||||
"defaultValue": ["10.0.0.0/24", "192.168.10.10"]
|
||||
},
|
||||
"effect": {
|
||||
"type": "String",
|
||||
"metadata": {
|
||||
"displayName": "Effect",
|
||||
"description": "Enable or disable the execution of the policy"
|
||||
},
|
||||
"allowedValues": ["deny", "audit", "disabled"],
|
||||
"defaultValue": "deny"
|
||||
}
|
||||
},
|
||||
"policyRule": {
|
||||
"if": {
|
||||
"count": {
|
||||
"field": "Microsoft.Storage/storageAccounts/networkAcls.ipRules[*]",
|
||||
"where": {
|
||||
"allOf": [
|
||||
{
|
||||
"field": "Microsoft.Storage/storageAccounts/networkAcls.ipRules[*].action",
|
||||
"equals": "Allow"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.Storage/storageAccounts/networkAcls.ipRules[*].value",
|
||||
"notIn": "[parameters('allowedIps')]"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"greater": 0
|
||||
},
|
||||
"then": {
|
||||
"effect": "[parameters('effect')]"
|
||||
}
|
||||
},
|
||||
"versions": ["1.0.0"]
|
||||
},
|
||||
"id": "/providers/Microsoft.Authorization/policyDefinitions/storage-ip-allowlist",
|
||||
"name": "storage-ip-allowlist",
|
||||
"type": "Microsoft.Authorization/policyDefinitions"
|
||||
}
|
||||
|
||||
cases:
|
||||
- note: provided_payload_allow1_does_not_match_action_filter
|
||||
resource:
|
||||
name: "storage-dev"
|
||||
type: "Microsoft.Storage/storageAccounts"
|
||||
location: "westus2"
|
||||
properties:
|
||||
networkAcls:
|
||||
ipRules:
|
||||
- value: "10.0.0.0/24"
|
||||
action: "Allow1"
|
||||
- value: "203.0.113.5"
|
||||
action: "Allow1"
|
||||
context: {}
|
||||
parameters: {}
|
||||
want_undefined: true
|
||||
|
||||
- note: deny_when_allow_action_and_ip_not_in_allowlist
|
||||
resource:
|
||||
name: "storage-dev"
|
||||
type: "Microsoft.Storage/storageAccounts"
|
||||
location: "westus2"
|
||||
properties:
|
||||
networkAcls:
|
||||
ipRules:
|
||||
- value: "10.0.0.0/24"
|
||||
action: "Allow"
|
||||
- value: "203.0.113.5"
|
||||
action: "Allow"
|
||||
want_effect: "deny"
|
||||
|
||||
- note: pass_when_all_allow_actions_are_in_allowlist
|
||||
resource:
|
||||
name: "storage-dev"
|
||||
type: "Microsoft.Storage/storageAccounts"
|
||||
location: "westus2"
|
||||
properties:
|
||||
networkAcls:
|
||||
ipRules:
|
||||
- value: "10.0.0.0/24"
|
||||
action: "Allow"
|
||||
- value: "192.168.10.10"
|
||||
action: "Allow"
|
||||
want_undefined: true
|
||||
125
tests/azure_policy/cases/e2e_storage_ip_allowlist_custom.yaml
Normal file
125
tests/azure_policy/cases/e2e_storage_ip_allowlist_custom.yaml
Normal file
@@ -0,0 +1,125 @@
|
||||
# Copyright (c) Microsoft Corporation.
|
||||
# Licensed under the MIT License.
|
||||
|
||||
# E2E Test: Custom Storage IP allowlist policy
|
||||
# Features: count.field + where(allOf) + notIn + parameterized effect/defaults
|
||||
|
||||
aliases: test_aliases.json
|
||||
|
||||
policy_definition: |
|
||||
{
|
||||
"properties": {
|
||||
"displayName": "Storage IP allowlist",
|
||||
"policyType": "Custom",
|
||||
"mode": "Indexed",
|
||||
"description": "Only allow IP rules that match an approved allowlist.",
|
||||
"metadata": {
|
||||
"version": "1.0.0",
|
||||
"category": "Network"
|
||||
},
|
||||
"version": "1.0.0",
|
||||
"parameters": {
|
||||
"allowedIps": {
|
||||
"type": "Array",
|
||||
"metadata": {
|
||||
"displayName": "Allowed IPs",
|
||||
"description": "IP addresses permitted in IP rules"
|
||||
},
|
||||
"defaultValue": ["10.0.0.0/24", "192.168.10.10"]
|
||||
},
|
||||
"effect": {
|
||||
"type": "String",
|
||||
"metadata": {
|
||||
"displayName": "Effect",
|
||||
"description": "Enable or disable the execution of the policy"
|
||||
},
|
||||
"allowedValues": ["deny", "audit", "disabled"],
|
||||
"defaultValue": "deny"
|
||||
}
|
||||
},
|
||||
"policyRule": {
|
||||
"if": {
|
||||
"count": {
|
||||
"field": "Microsoft.Storage/storageAccounts/networkAcls.ipRules[*]",
|
||||
"where": {
|
||||
"allOf": [
|
||||
{
|
||||
"field": "Microsoft.Storage/storageAccounts/networkAcls.ipRules[*].action",
|
||||
"equals": "Allow"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.Storage/storageAccounts/networkAcls.ipRules[*].value",
|
||||
"notIn": "[parameters('allowedIps')]"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"greater": 0
|
||||
},
|
||||
"then": {
|
||||
"effect": "[parameters('effect')]"
|
||||
}
|
||||
},
|
||||
"versions": ["1.0.0"]
|
||||
},
|
||||
"id": "/providers/Microsoft.Authorization/policyDefinitions/storage-ip-allowlist",
|
||||
"name": "storage-ip-allowlist",
|
||||
"type": "Microsoft.Authorization/policyDefinitions"
|
||||
}
|
||||
|
||||
cases:
|
||||
- note: pass_when_action_is_not_exact_allow
|
||||
resource:
|
||||
name: "storage-dev"
|
||||
type: "Microsoft.Storage/storageAccounts"
|
||||
location: "westus2"
|
||||
properties:
|
||||
networkAcls:
|
||||
ipRules:
|
||||
- value: "10.0.0.0/24"
|
||||
action: "Allow1"
|
||||
- value: "203.0.113.5"
|
||||
action: "Allow1"
|
||||
want_undefined: true
|
||||
|
||||
- note: deny_when_disallowed_ip_has_allow_action
|
||||
resource:
|
||||
name: "storage-dev"
|
||||
type: "Microsoft.Storage/storageAccounts"
|
||||
location: "westus2"
|
||||
properties:
|
||||
networkAcls:
|
||||
ipRules:
|
||||
- value: "10.0.0.0/24"
|
||||
action: "Allow"
|
||||
- value: "203.0.113.5"
|
||||
action: "Allow"
|
||||
want_effect: "deny"
|
||||
|
||||
- note: pass_when_all_allow_ips_are_in_allowlist
|
||||
resource:
|
||||
name: "storage-dev"
|
||||
type: "Microsoft.Storage/storageAccounts"
|
||||
location: "westus2"
|
||||
properties:
|
||||
networkAcls:
|
||||
ipRules:
|
||||
- value: "10.0.0.0/24"
|
||||
action: "Allow"
|
||||
- value: "192.168.10.10"
|
||||
action: "Allow"
|
||||
want_undefined: true
|
||||
|
||||
- note: audit_when_effect_parameter_is_overridden
|
||||
resource:
|
||||
name: "storage-dev"
|
||||
type: "Microsoft.Storage/storageAccounts"
|
||||
location: "westus2"
|
||||
properties:
|
||||
networkAcls:
|
||||
ipRules:
|
||||
- value: "203.0.113.5"
|
||||
action: "Allow"
|
||||
parameters:
|
||||
effect: "audit"
|
||||
want_effect: "audit"
|
||||
158
tests/azure_policy/cases/e2e_storage_public_blob_modify.yaml
Normal file
158
tests/azure_policy/cases/e2e_storage_public_blob_modify.yaml
Normal file
@@ -0,0 +1,158 @@
|
||||
# Copyright (c) Microsoft Corporation.
|
||||
# Licensed under the MIT License.
|
||||
|
||||
# E2E Test: Storage/StorageAccountDisablePublicBlobAccess_Modify
|
||||
# Real Azure Policy: "Configure your Storage account public access to be disallowed"
|
||||
# Source: regolator/policyDefinitions/Storage/StorageAccountDisablePublicBlobAccess_Modify.json
|
||||
#
|
||||
# Features exercised:
|
||||
# - requestContext().apiVersion less / greaterOrEquals comparisons
|
||||
# - exists "true" combined with value condition in allOf
|
||||
# - Modify with requestContext().apiVersion operation condition
|
||||
# - conflictEffect: audit
|
||||
|
||||
aliases: test_aliases.json
|
||||
|
||||
policy_definition: |
|
||||
{
|
||||
"properties": {
|
||||
"displayName": "Configure your Storage account public access to be disallowed",
|
||||
"policyType": "BuiltIn",
|
||||
"mode": "Indexed",
|
||||
"parameters": {
|
||||
"effect": {
|
||||
"type": "String",
|
||||
"allowedValues": ["Modify", "Disabled"],
|
||||
"defaultValue": "Modify"
|
||||
}
|
||||
},
|
||||
"policyRule": {
|
||||
"if": {
|
||||
"allOf": [
|
||||
{
|
||||
"field": "type",
|
||||
"equals": "Microsoft.Storage/storageAccounts"
|
||||
},
|
||||
{
|
||||
"anyOf": [
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"value": "[requestContext().apiVersion]",
|
||||
"less": "2019-04-01"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.Storage/storageAccounts/allowBlobPublicAccess",
|
||||
"exists": "true"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.Storage/storageAccounts/allowBlobPublicAccess",
|
||||
"equals": "true"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"then": {
|
||||
"effect": "[parameters('effect')]",
|
||||
"details": {
|
||||
"conflictEffect": "audit",
|
||||
"roleDefinitionIds": [
|
||||
"/providers/Microsoft.Authorization/roleDefinitions/17d1049b-9a84-46fb-8f53-869881c3d3ab"
|
||||
],
|
||||
"operations": [
|
||||
{
|
||||
"condition": "[greaterOrEquals(requestContext().apiVersion, '2019-04-01')]",
|
||||
"operation": "addOrReplace",
|
||||
"field": "Microsoft.Storage/storageAccounts/allowBlobPublicAccess",
|
||||
"value": false
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cases:
|
||||
# =========================================================================
|
||||
# allowBlobPublicAccess = true, modern API → Modify
|
||||
# =========================================================================
|
||||
|
||||
- note: modify_public_blob_enabled_modern_api
|
||||
resource:
|
||||
type: "Microsoft.Storage/storageAccounts"
|
||||
name: "stg1"
|
||||
properties:
|
||||
allowBlobPublicAccess: true
|
||||
api_version: "2021-02-01"
|
||||
want_effect: "Modify"
|
||||
want_details:
|
||||
roleDefinitionIds:
|
||||
- "/providers/Microsoft.Authorization/roleDefinitions/17d1049b-9a84-46fb-8f53-869881c3d3ab"
|
||||
operations:
|
||||
- condition: "[greaterOrEquals(requestContext().apiVersion, '2019-04-01')]"
|
||||
operation: "addOrReplace"
|
||||
field: "Microsoft.Storage/storageAccounts/allowBlobPublicAccess"
|
||||
value: false
|
||||
|
||||
# =========================================================================
|
||||
# Old API version + field exists → triggers via the less branch
|
||||
# (requestContext().apiVersion < "2019-04-01" AND field exists → Modify)
|
||||
# =========================================================================
|
||||
|
||||
- note: modify_old_api_field_exists
|
||||
resource:
|
||||
type: "Microsoft.Storage/storageAccounts"
|
||||
name: "stg2"
|
||||
properties:
|
||||
allowBlobPublicAccess: false
|
||||
api_version: "2018-11-01"
|
||||
want_effect: "Modify"
|
||||
want_details:
|
||||
roleDefinitionIds:
|
||||
- "/providers/Microsoft.Authorization/roleDefinitions/17d1049b-9a84-46fb-8f53-869881c3d3ab"
|
||||
operations:
|
||||
- condition: "[greaterOrEquals(requestContext().apiVersion, '2019-04-01')]"
|
||||
operation: "addOrReplace"
|
||||
field: "Microsoft.Storage/storageAccounts/allowBlobPublicAccess"
|
||||
value: false
|
||||
|
||||
# =========================================================================
|
||||
# Old API version but field doesn't exist → pass
|
||||
# =========================================================================
|
||||
|
||||
- note: pass_old_api_field_missing
|
||||
resource:
|
||||
type: "Microsoft.Storage/storageAccounts"
|
||||
name: "stg3"
|
||||
properties: {}
|
||||
api_version: "2018-11-01"
|
||||
want_undefined: true
|
||||
|
||||
# =========================================================================
|
||||
# allowBlobPublicAccess = false, modern API → pass
|
||||
# =========================================================================
|
||||
|
||||
- note: pass_public_blob_disabled
|
||||
resource:
|
||||
type: "Microsoft.Storage/storageAccounts"
|
||||
name: "stg4"
|
||||
properties:
|
||||
allowBlobPublicAccess: false
|
||||
api_version: "2021-02-01"
|
||||
want_undefined: true
|
||||
|
||||
# =========================================================================
|
||||
# Wrong resource type → pass
|
||||
# =========================================================================
|
||||
|
||||
- note: pass_wrong_type
|
||||
resource:
|
||||
type: "Microsoft.Compute/virtualMachines"
|
||||
name: "vm1"
|
||||
properties: {}
|
||||
api_version: "2021-03-01"
|
||||
want_undefined: true
|
||||
180
tests/azure_policy/cases/e2e_storage_vnet_rules.yaml
Normal file
180
tests/azure_policy/cases/e2e_storage_vnet_rules.yaml
Normal file
@@ -0,0 +1,180 @@
|
||||
# Copyright (c) Microsoft Corporation.
|
||||
# Licensed under the MIT License.
|
||||
|
||||
# E2E Test: Storage/StorageAccountOnlyVnetRulesEnabled_Audit
|
||||
# Real Azure Policy: "Storage accounts should restrict network access using
|
||||
# virtual network rules"
|
||||
# Features: allOf, anyOf, field (type + alias), equals, notEquals, field count
|
||||
# (array [*] without where), greaterOrEquals, parameters() with defaultValue
|
||||
|
||||
aliases: test_aliases.json
|
||||
|
||||
policy_definition: |
|
||||
{
|
||||
"properties": {
|
||||
"displayName": "Storage accounts should restrict network access using virtual network rules",
|
||||
"policyType": "BuiltIn",
|
||||
"mode": "Indexed",
|
||||
"parameters": {
|
||||
"effect": {
|
||||
"type": "String",
|
||||
"metadata": {
|
||||
"displayName": "Effect",
|
||||
"description": "Enable or disable the execution of the audit policy"
|
||||
},
|
||||
"allowedValues": ["Audit", "Deny", "Disabled"],
|
||||
"defaultValue": "Audit"
|
||||
}
|
||||
},
|
||||
"policyRule": {
|
||||
"if": {
|
||||
"allOf": [
|
||||
{
|
||||
"field": "type",
|
||||
"equals": "Microsoft.Storage/storageAccounts"
|
||||
},
|
||||
{
|
||||
"anyOf": [
|
||||
{
|
||||
"field": "Microsoft.Storage/storageAccounts/networkAcls.defaultAction",
|
||||
"notEquals": "Deny"
|
||||
},
|
||||
{
|
||||
"count": {
|
||||
"field": "Microsoft.Storage/storageAccounts/networkAcls.ipRules[*]"
|
||||
},
|
||||
"greaterOrEquals": 1
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"then": {
|
||||
"effect": "[parameters('effect')]"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cases:
|
||||
# =========================================================================
|
||||
# Audit — default action is Allow (not Deny)
|
||||
# =========================================================================
|
||||
|
||||
- note: audit_default_action_allow
|
||||
resource:
|
||||
type: "Microsoft.Storage/storageAccounts"
|
||||
name: "openStorage"
|
||||
location: "eastus"
|
||||
properties:
|
||||
networkAcls:
|
||||
defaultAction: "Allow"
|
||||
ipRules: []
|
||||
want_effect: "Audit"
|
||||
|
||||
# =========================================================================
|
||||
# Audit — default action is Deny but has IP rules
|
||||
# =========================================================================
|
||||
|
||||
- note: audit_deny_with_ip_rules
|
||||
resource:
|
||||
type: "Microsoft.Storage/storageAccounts"
|
||||
name: "restrictedStorage"
|
||||
location: "eastus"
|
||||
properties:
|
||||
networkAcls:
|
||||
defaultAction: "Deny"
|
||||
ipRules:
|
||||
- value: "10.0.0.0/24"
|
||||
action: "Allow"
|
||||
want_effect: "Audit"
|
||||
|
||||
# =========================================================================
|
||||
# No effect — default action is Deny and no IP rules
|
||||
# =========================================================================
|
||||
|
||||
- note: pass_deny_no_ip_rules
|
||||
resource:
|
||||
type: "Microsoft.Storage/storageAccounts"
|
||||
name: "vnetOnlyStorage"
|
||||
location: "eastus"
|
||||
properties:
|
||||
networkAcls:
|
||||
defaultAction: "Deny"
|
||||
ipRules: []
|
||||
want_undefined: true
|
||||
|
||||
# =========================================================================
|
||||
# Audit — both violations: default Allow AND has IP rules
|
||||
# =========================================================================
|
||||
|
||||
- note: audit_allow_with_ip_rules
|
||||
resource:
|
||||
type: "Microsoft.Storage/storageAccounts"
|
||||
name: "wideOpenStorage"
|
||||
location: "westus"
|
||||
properties:
|
||||
networkAcls:
|
||||
defaultAction: "Allow"
|
||||
ipRules:
|
||||
- value: "192.168.1.0/24"
|
||||
action: "Allow"
|
||||
- value: "10.0.0.1"
|
||||
action: "Allow"
|
||||
want_effect: "Audit"
|
||||
|
||||
# =========================================================================
|
||||
# Audit — networkAcls missing entirely (defaultAction undefined → notEquals
|
||||
# "Deny" is true for null/undefined)
|
||||
# =========================================================================
|
||||
|
||||
- note: audit_no_network_acls
|
||||
resource:
|
||||
type: "Microsoft.Storage/storageAccounts"
|
||||
name: "noAclStorage"
|
||||
location: "eastus"
|
||||
properties: {}
|
||||
want_effect: "Audit"
|
||||
|
||||
# =========================================================================
|
||||
# No effect — wrong resource type
|
||||
# =========================================================================
|
||||
|
||||
- note: skip_wrong_type
|
||||
resource:
|
||||
type: "Microsoft.Compute/virtualMachines"
|
||||
name: "myVM"
|
||||
location: "eastus"
|
||||
properties: {}
|
||||
want_undefined: true
|
||||
|
||||
# =========================================================================
|
||||
# Deny — explicit effect parameter override
|
||||
# =========================================================================
|
||||
|
||||
- note: deny_with_explicit_effect
|
||||
resource:
|
||||
type: "Microsoft.Storage/storageAccounts"
|
||||
name: "openStorage"
|
||||
location: "eastus"
|
||||
properties:
|
||||
networkAcls:
|
||||
defaultAction: "Allow"
|
||||
ipRules: []
|
||||
parameters:
|
||||
effect: "Deny"
|
||||
want_effect: "Deny"
|
||||
|
||||
# =========================================================================
|
||||
# No effect — Deny action, ipRules key missing (count over undefined = 0)
|
||||
# =========================================================================
|
||||
|
||||
- note: pass_deny_no_ip_rules_key
|
||||
resource:
|
||||
type: "Microsoft.Storage/storageAccounts"
|
||||
name: "minimalStorage"
|
||||
location: "eastus"
|
||||
properties:
|
||||
networkAcls:
|
||||
defaultAction: "Deny"
|
||||
want_undefined: true
|
||||
428
tests/azure_policy/cases/e2e_stream_analytics_audit.yaml
Normal file
428
tests/azure_policy/cases/e2e_stream_analytics_audit.yaml
Normal file
@@ -0,0 +1,428 @@
|
||||
# Copyright (c) Microsoft Corporation.
|
||||
# Licensed under the MIT License.
|
||||
|
||||
# E2E Test: Stream Analytics/DataExfiltration_Audit
|
||||
# Real Azure Policy: "Stream Analytics job should connect to trusted inputs
|
||||
# and outputs"
|
||||
# Source: regolator/policyDefinitions/Stream Analytics/DataExfiltration_Audit.json
|
||||
#
|
||||
# Features exercised:
|
||||
# - anyOf tree (2 top-level branches for outputs and streamingjobs)
|
||||
# - Multiple datasource type checks with notIn on sub-resource types
|
||||
# - Count with where clause on wildcard array (storageAccounts[*].accountName)
|
||||
# - notIn operator with parameterised Array
|
||||
# - Long alias paths with dots and hyphens (e.g. datasource.Microsoft-Storage-Blob.storageAccounts[*])
|
||||
# - exists "true" + notIn combined check
|
||||
|
||||
aliases: test_aliases.json
|
||||
|
||||
policy_definition: |
|
||||
{
|
||||
"properties": {
|
||||
"displayName": "Stream Analytics job should connect to trusted inputs and outputs",
|
||||
"policyType": "BuiltIn",
|
||||
"mode": "All",
|
||||
"parameters": {
|
||||
"effect": {
|
||||
"type": "String",
|
||||
"allowedValues": ["Deny", "Disabled", "Audit"],
|
||||
"defaultValue": "Audit"
|
||||
},
|
||||
"allowedEventHubNamespaces": {
|
||||
"type": "Array",
|
||||
"defaultValue": []
|
||||
},
|
||||
"allowedSQLServers": {
|
||||
"type": "Array",
|
||||
"defaultValue": []
|
||||
},
|
||||
"allowedStorageAccounts": {
|
||||
"type": "Array",
|
||||
"defaultValue": []
|
||||
},
|
||||
"allowedCosmosAccounts": {
|
||||
"type": "Array",
|
||||
"defaultValue": []
|
||||
},
|
||||
"allowedAzureFunctionAccounts": {
|
||||
"type": "Array",
|
||||
"defaultValue": []
|
||||
},
|
||||
"allowedIoTHubNamespaces": {
|
||||
"type": "Array",
|
||||
"defaultValue": []
|
||||
},
|
||||
"allowedMLWebServiceEndpoints": {
|
||||
"type": "Array",
|
||||
"defaultValue": []
|
||||
}
|
||||
},
|
||||
"policyRule": {
|
||||
"if": {
|
||||
"anyOf": [
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"field": "type",
|
||||
"equals": "Microsoft.StreamAnalytics/streamingjobs/outputs"
|
||||
},
|
||||
{
|
||||
"anyOf": [
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"field": "Microsoft.StreamAnalytics/streamingjobs/outputs/datasource.type",
|
||||
"equals": "Microsoft.EventHub/EventHub"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.StreamAnalytics/streamingjobs/outputs/datasource.Microsoft-ServiceBus-EventHub.serviceBusNamespace",
|
||||
"notIn": "[parameters('allowedEventHubNamespaces')]"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"field": "Microsoft.StreamAnalytics/streamingjobs/outputs/datasource.type",
|
||||
"equals": "Microsoft.Sql/Server/Database"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.StreamAnalytics/streamingjobs/outputs/datasource.Microsoft-Sql-Server-Database.server",
|
||||
"notIn": "[parameters('allowedSQLServers')]"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"field": "Microsoft.StreamAnalytics/streamingjobs/outputs/datasource.type",
|
||||
"equals": "Microsoft.Storage/Table"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.StreamAnalytics/streamingjobs/outputs/datasource.Microsoft-Storage-Table.accountName",
|
||||
"notIn": "[parameters('allowedStorageAccounts')]"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"field": "Microsoft.StreamAnalytics/streamingjobs/outputs/datasource.type",
|
||||
"equals": "Microsoft.Storage/DocumentDB"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.StreamAnalytics/streamingjobs/outputs/datasource.Microsoft-Storage-DocumentDB.accountId",
|
||||
"notIn": "[parameters('allowedCosmosAccounts')]"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"field": "Microsoft.StreamAnalytics/streamingjobs/outputs/datasource.type",
|
||||
"equals": "Microsoft.AzureFunction"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.StreamAnalytics/streamingjobs/outputs/datasource.Microsoft-AzureFunction.functionAppName",
|
||||
"notIn": "[parameters('allowedAzureFunctionAccounts')]"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"field": "Microsoft.StreamAnalytics/streamingjobs/outputs/datasource.type",
|
||||
"equals": "Microsoft.Storage/Blob"
|
||||
},
|
||||
{
|
||||
"count": {
|
||||
"field": "Microsoft.StreamAnalytics/streamingjobs/outputs/datasource.Microsoft-Storage-Blob.storageAccounts[*]",
|
||||
"where": {
|
||||
"field": "Microsoft.StreamAnalytics/streamingjobs/outputs/datasource.Microsoft-Storage-Blob.storageAccounts[*].accountName",
|
||||
"notIn": "[parameters('allowedStorageAccounts')]"
|
||||
}
|
||||
},
|
||||
"greater": 0
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.StreamAnalytics/streamingjobs/outputs/datasource.type",
|
||||
"notIn": [
|
||||
"Microsoft.EventHub/EventHub",
|
||||
"Microsoft.Sql/Server/Database",
|
||||
"Microsoft.Storage/Table",
|
||||
"Microsoft.Storage/DocumentDB",
|
||||
"Microsoft.AzureFunction",
|
||||
"Microsoft.Storage/Blob"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"field": "type",
|
||||
"equals": "Microsoft.StreamAnalytics/streamingjobs"
|
||||
},
|
||||
{
|
||||
"anyOf": [
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"field": "Microsoft.StreamAnalytics/streamingjobs/jobStorageAccount",
|
||||
"exists": "true"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.StreamAnalytics/streamingjobs/jobStorageAccount.accountName",
|
||||
"notIn": "[parameters('allowedStorageAccounts')]"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"then": {
|
||||
"effect": "[parameters('effect')]"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cases:
|
||||
# =========================================================================
|
||||
# OUTPUT: EventHub output to allowed namespace → pass
|
||||
# =========================================================================
|
||||
|
||||
- note: pass_output_eventhub_allowed
|
||||
resource:
|
||||
type: "Microsoft.StreamAnalytics/streamingjobs/outputs"
|
||||
name: "output1"
|
||||
properties:
|
||||
datasource:
|
||||
type: "Microsoft.EventHub/EventHub"
|
||||
Microsoft-ServiceBus-EventHub:
|
||||
serviceBusNamespace: "my-eh-namespace"
|
||||
parameters:
|
||||
allowedEventHubNamespaces:
|
||||
- "my-eh-namespace"
|
||||
want_undefined: true
|
||||
|
||||
# =========================================================================
|
||||
# OUTPUT: EventHub output to disallowed namespace → Audit
|
||||
# =========================================================================
|
||||
|
||||
- note: audit_output_eventhub_disallowed
|
||||
resource:
|
||||
type: "Microsoft.StreamAnalytics/streamingjobs/outputs"
|
||||
name: "output2"
|
||||
properties:
|
||||
datasource:
|
||||
type: "Microsoft.EventHub/EventHub"
|
||||
Microsoft-ServiceBus-EventHub:
|
||||
serviceBusNamespace: "rogue-namespace"
|
||||
parameters:
|
||||
allowedEventHubNamespaces:
|
||||
- "trusted-namespace"
|
||||
want_effect: "Audit"
|
||||
|
||||
# =========================================================================
|
||||
# OUTPUT: SQL output to allowed server → pass
|
||||
# =========================================================================
|
||||
|
||||
- note: pass_output_sql_allowed
|
||||
resource:
|
||||
type: "Microsoft.StreamAnalytics/streamingjobs/outputs"
|
||||
name: "output-sql"
|
||||
properties:
|
||||
datasource:
|
||||
type: "Microsoft.Sql/Server/Database"
|
||||
Microsoft-Sql-Server-Database:
|
||||
server: "sql-server-approved"
|
||||
parameters:
|
||||
allowedSQLServers:
|
||||
- "sql-server-approved"
|
||||
want_undefined: true
|
||||
|
||||
# =========================================================================
|
||||
# OUTPUT: Blob storage with disallowed account in storageAccounts[*] → Audit
|
||||
# =========================================================================
|
||||
|
||||
- note: audit_output_blob_disallowed
|
||||
resource:
|
||||
type: "Microsoft.StreamAnalytics/streamingjobs/outputs"
|
||||
name: "output-blob"
|
||||
properties:
|
||||
datasource:
|
||||
type: "Microsoft.Storage/Blob"
|
||||
Microsoft-Storage-Blob:
|
||||
storageAccounts:
|
||||
- accountName: "rogue-storage"
|
||||
- accountName: "trusted-storage"
|
||||
parameters:
|
||||
allowedStorageAccounts:
|
||||
- "trusted-storage"
|
||||
want_effect: "Audit"
|
||||
|
||||
# =========================================================================
|
||||
# OUTPUT: Blob storage with all accounts allowed → pass
|
||||
# =========================================================================
|
||||
|
||||
- note: pass_output_blob_all_allowed
|
||||
resource:
|
||||
type: "Microsoft.StreamAnalytics/streamingjobs/outputs"
|
||||
name: "output-blob-ok"
|
||||
properties:
|
||||
datasource:
|
||||
type: "Microsoft.Storage/Blob"
|
||||
Microsoft-Storage-Blob:
|
||||
storageAccounts:
|
||||
- accountName: "trusted-storage"
|
||||
parameters:
|
||||
allowedStorageAccounts:
|
||||
- "trusted-storage"
|
||||
want_undefined: true
|
||||
|
||||
# =========================================================================
|
||||
# OUTPUT: SQL output to disallowed server → Audit
|
||||
# =========================================================================
|
||||
|
||||
- note: audit_output_sql_disallowed
|
||||
resource:
|
||||
type: "Microsoft.StreamAnalytics/streamingjobs/outputs"
|
||||
name: "output-sql-bad"
|
||||
properties:
|
||||
datasource:
|
||||
type: "Microsoft.Sql/Server/Database"
|
||||
Microsoft-Sql-Server-Database:
|
||||
server: "rogue-sql-server"
|
||||
parameters:
|
||||
allowedSQLServers:
|
||||
- "trusted-sql-server"
|
||||
want_effect: "Audit"
|
||||
|
||||
# =========================================================================
|
||||
# OUTPUT: Table output to disallowed account → Audit
|
||||
# =========================================================================
|
||||
|
||||
- note: audit_output_table_disallowed
|
||||
resource:
|
||||
type: "Microsoft.StreamAnalytics/streamingjobs/outputs"
|
||||
name: "output-table"
|
||||
properties:
|
||||
datasource:
|
||||
type: "Microsoft.Storage/Table"
|
||||
Microsoft-Storage-Table:
|
||||
accountName: "rogue-storage"
|
||||
parameters:
|
||||
allowedStorageAccounts:
|
||||
- "trusted-storage"
|
||||
want_effect: "Audit"
|
||||
|
||||
# =========================================================================
|
||||
# OUTPUT: CosmosDB output to disallowed account → Audit
|
||||
# =========================================================================
|
||||
|
||||
- note: audit_output_cosmosdb_disallowed
|
||||
resource:
|
||||
type: "Microsoft.StreamAnalytics/streamingjobs/outputs"
|
||||
name: "output-cosmosdb"
|
||||
properties:
|
||||
datasource:
|
||||
type: "Microsoft.Storage/DocumentDB"
|
||||
Microsoft-Storage-DocumentDB:
|
||||
accountId: "rogue-cosmos-account"
|
||||
parameters:
|
||||
allowedCosmosAccounts:
|
||||
- "trusted-cosmos-account"
|
||||
want_effect: "Audit"
|
||||
|
||||
# =========================================================================
|
||||
# OUTPUT: AzureFunction output to disallowed function app → Audit
|
||||
# =========================================================================
|
||||
|
||||
- note: audit_output_function_disallowed
|
||||
resource:
|
||||
type: "Microsoft.StreamAnalytics/streamingjobs/outputs"
|
||||
name: "output-function"
|
||||
properties:
|
||||
datasource:
|
||||
type: "Microsoft.AzureFunction"
|
||||
Microsoft-AzureFunction:
|
||||
functionAppName: "rogue-function-app"
|
||||
parameters:
|
||||
allowedAzureFunctionAccounts:
|
||||
- "trusted-function-app"
|
||||
want_effect: "Audit"
|
||||
|
||||
# =========================================================================
|
||||
# OUTPUT: Unknown datasource type → Audit (not in known list)
|
||||
# =========================================================================
|
||||
|
||||
- note: audit_output_unknown_type
|
||||
resource:
|
||||
type: "Microsoft.StreamAnalytics/streamingjobs/outputs"
|
||||
name: "output-unknown"
|
||||
properties:
|
||||
datasource:
|
||||
type: "Microsoft.SomeNewService/SomeType"
|
||||
want_effect: "Audit"
|
||||
|
||||
# =========================================================================
|
||||
# STREAMINGJOB: jobStorageAccount with disallowed account → Audit
|
||||
# =========================================================================
|
||||
|
||||
- note: audit_job_storage_disallowed
|
||||
resource:
|
||||
type: "Microsoft.StreamAnalytics/streamingjobs"
|
||||
name: "job1"
|
||||
properties:
|
||||
jobStorageAccount:
|
||||
accountName: "rogue-storage"
|
||||
parameters:
|
||||
allowedStorageAccounts:
|
||||
- "trusted-storage"
|
||||
want_effect: "Audit"
|
||||
|
||||
# =========================================================================
|
||||
# STREAMINGJOB: jobStorageAccount with allowed account → pass
|
||||
# =========================================================================
|
||||
|
||||
- note: pass_job_storage_allowed
|
||||
resource:
|
||||
type: "Microsoft.StreamAnalytics/streamingjobs"
|
||||
name: "job2"
|
||||
properties:
|
||||
jobStorageAccount:
|
||||
accountName: "trusted-storage"
|
||||
parameters:
|
||||
allowedStorageAccounts:
|
||||
- "trusted-storage"
|
||||
want_undefined: true
|
||||
|
||||
# =========================================================================
|
||||
# STREAMINGJOB: no jobStorageAccount → pass (exists "true" fails)
|
||||
# =========================================================================
|
||||
|
||||
- note: pass_job_no_storage
|
||||
resource:
|
||||
type: "Microsoft.StreamAnalytics/streamingjobs"
|
||||
name: "job3"
|
||||
properties: {}
|
||||
want_undefined: true
|
||||
|
||||
# =========================================================================
|
||||
# Wrong type entirely → pass
|
||||
# =========================================================================
|
||||
|
||||
- note: pass_wrong_type
|
||||
resource:
|
||||
type: "Microsoft.Compute/virtualMachines"
|
||||
name: "vm1"
|
||||
properties: {}
|
||||
want_undefined: true
|
||||
115
tests/azure_policy/cases/e2e_tags_append.yaml
Normal file
115
tests/azure_policy/cases/e2e_tags_append.yaml
Normal file
@@ -0,0 +1,115 @@
|
||||
# Copyright (c) Microsoft Corporation.
|
||||
# Licensed under the MIT License.
|
||||
|
||||
# E2E Test: Tags/ApplyTag_Append
|
||||
# Real Azure Policy: "Append a tag and its value to resources"
|
||||
# Source: regolator/policyDefinitions/Tags/ApplyTag_Append.json
|
||||
#
|
||||
# Features exercised:
|
||||
# - Append effect with details array
|
||||
# - concat() for dynamic tag field
|
||||
# - exists operator ("false")
|
||||
# - No type filter (applies to all resource types)
|
||||
|
||||
aliases: test_aliases.json
|
||||
|
||||
policy_definition: |
|
||||
{
|
||||
"properties": {
|
||||
"displayName": "Append a tag and its value to resources",
|
||||
"policyType": "BuiltIn",
|
||||
"mode": "Indexed",
|
||||
"parameters": {
|
||||
"tagName": {
|
||||
"type": "String"
|
||||
},
|
||||
"tagValue": {
|
||||
"type": "String"
|
||||
}
|
||||
},
|
||||
"policyRule": {
|
||||
"if": {
|
||||
"field": "[concat('tags[', parameters('tagName'), ']')]",
|
||||
"exists": "false"
|
||||
},
|
||||
"then": {
|
||||
"effect": "append",
|
||||
"details": [
|
||||
{
|
||||
"field": "[concat('tags[', parameters('tagName'), ']')]",
|
||||
"value": "[parameters('tagValue')]"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cases:
|
||||
# =========================================================================
|
||||
# Tag missing → append
|
||||
# =========================================================================
|
||||
|
||||
- note: append_tag_missing
|
||||
resource:
|
||||
type: "Microsoft.Compute/virtualMachines"
|
||||
name: "vm-no-env"
|
||||
properties:
|
||||
tags: {}
|
||||
parameters:
|
||||
tagName: "environment"
|
||||
tagValue: "production"
|
||||
want_effect: "append"
|
||||
want_details:
|
||||
- field: "[concat('tags[', parameters('tagName'), ']')]"
|
||||
value: "production"
|
||||
|
||||
- note: append_no_tags_at_all
|
||||
resource:
|
||||
type: "Microsoft.Compute/virtualMachines"
|
||||
name: "vm-no-tags"
|
||||
properties: {}
|
||||
parameters:
|
||||
tagName: "costCenter"
|
||||
tagValue: "12345"
|
||||
want_effect: "append"
|
||||
|
||||
- note: append_other_tags_exist
|
||||
resource:
|
||||
type: "Microsoft.Storage/storageAccounts"
|
||||
name: "sa-partial-tags"
|
||||
properties:
|
||||
tags:
|
||||
owner: "alice"
|
||||
parameters:
|
||||
tagName: "environment"
|
||||
tagValue: "staging"
|
||||
want_effect: "append"
|
||||
|
||||
# =========================================================================
|
||||
# Tag already exists → pass
|
||||
# =========================================================================
|
||||
|
||||
- note: pass_tag_already_exists
|
||||
resource:
|
||||
type: "Microsoft.Compute/virtualMachines"
|
||||
name: "vm-has-env"
|
||||
properties:
|
||||
tags:
|
||||
environment: "dev"
|
||||
parameters:
|
||||
tagName: "environment"
|
||||
tagValue: "production"
|
||||
want_undefined: true
|
||||
|
||||
- note: pass_tag_matches_value
|
||||
resource:
|
||||
type: "Microsoft.Compute/virtualMachines"
|
||||
name: "vm-correct"
|
||||
properties:
|
||||
tags:
|
||||
environment: "production"
|
||||
parameters:
|
||||
tagName: "environment"
|
||||
tagValue: "production"
|
||||
want_undefined: true
|
||||
147
tests/azure_policy/cases/e2e_tags_inherit_modify.yaml
Normal file
147
tests/azure_policy/cases/e2e_tags_inherit_modify.yaml
Normal file
@@ -0,0 +1,147 @@
|
||||
# Copyright (c) Microsoft Corporation.
|
||||
# Licensed under the MIT License.
|
||||
|
||||
# E2E Test: Tags/InheritTag_AddOrReplace_Modify
|
||||
# Real Azure Policy: "Inherit a tag from the resource group"
|
||||
# Source: regolator/policyDefinitions/Tags/InheritTag_AddOrReplace_Modify.json
|
||||
#
|
||||
# Features exercised:
|
||||
# - resourceGroup() template function
|
||||
# - concat() to build dynamic tag field path: tags[<tagName>]
|
||||
# - Parameter-derived field names
|
||||
# - Modify effect with addOrReplace operation
|
||||
# - value condition with resourceGroup().tags[parameters('tagName')]
|
||||
|
||||
aliases: test_aliases.json
|
||||
|
||||
policy_definition: |
|
||||
{
|
||||
"properties": {
|
||||
"displayName": "Inherit a tag from the resource group",
|
||||
"policyType": "BuiltIn",
|
||||
"mode": "Indexed",
|
||||
"parameters": {
|
||||
"tagName": {
|
||||
"type": "String",
|
||||
"metadata": {
|
||||
"displayName": "Tag Name",
|
||||
"description": "Name of the tag, such as 'environment'"
|
||||
}
|
||||
}
|
||||
},
|
||||
"policyRule": {
|
||||
"if": {
|
||||
"allOf": [
|
||||
{
|
||||
"field": "[concat('tags[', parameters('tagName'), ']')]",
|
||||
"notEquals": "[resourceGroup().tags[parameters('tagName')]]"
|
||||
},
|
||||
{
|
||||
"value": "[resourceGroup().tags[parameters('tagName')]]",
|
||||
"notEquals": ""
|
||||
}
|
||||
]
|
||||
},
|
||||
"then": {
|
||||
"effect": "modify",
|
||||
"details": {
|
||||
"roleDefinitionIds": [
|
||||
"/providers/microsoft.authorization/roleDefinitions/b24988ac-6180-42a0-ab88-20f7382dd24c"
|
||||
],
|
||||
"operations": [
|
||||
{
|
||||
"operation": "addOrReplace",
|
||||
"field": "[concat('tags[', parameters('tagName'), ']')]",
|
||||
"value": "[resourceGroup().tags[parameters('tagName')]]"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cases:
|
||||
# =========================================================================
|
||||
# Resource tag differs from resource group tag → modify
|
||||
# =========================================================================
|
||||
|
||||
- note: modify_tag_differs
|
||||
resource:
|
||||
type: "Microsoft.Compute/virtualMachines"
|
||||
name: "vm-wrong-env"
|
||||
properties:
|
||||
tags:
|
||||
environment: "dev"
|
||||
parameters:
|
||||
tagName: "environment"
|
||||
context:
|
||||
resourceGroup:
|
||||
tags:
|
||||
environment: "production"
|
||||
want_effect: "modify"
|
||||
|
||||
- note: modify_tag_missing_on_resource
|
||||
resource:
|
||||
type: "Microsoft.Compute/virtualMachines"
|
||||
name: "vm-no-tag"
|
||||
properties: {}
|
||||
parameters:
|
||||
tagName: "environment"
|
||||
context:
|
||||
resourceGroup:
|
||||
tags:
|
||||
environment: "production"
|
||||
want_effect: "modify"
|
||||
|
||||
# =========================================================================
|
||||
# Resource tag matches resource group tag → pass
|
||||
# =========================================================================
|
||||
|
||||
- note: pass_tag_matches
|
||||
resource:
|
||||
type: "Microsoft.Compute/virtualMachines"
|
||||
name: "vm-correct-env"
|
||||
properties:
|
||||
tags:
|
||||
environment: "production"
|
||||
parameters:
|
||||
tagName: "environment"
|
||||
context:
|
||||
resourceGroup:
|
||||
tags:
|
||||
environment: "production"
|
||||
want_undefined: true
|
||||
|
||||
# =========================================================================
|
||||
# Resource group tag is empty → pass (second condition fails)
|
||||
# =========================================================================
|
||||
|
||||
- note: pass_rg_tag_empty
|
||||
resource:
|
||||
type: "Microsoft.Compute/virtualMachines"
|
||||
name: "vm-rg-empty"
|
||||
properties:
|
||||
tags:
|
||||
environment: "dev"
|
||||
parameters:
|
||||
tagName: "environment"
|
||||
context:
|
||||
resourceGroup:
|
||||
tags:
|
||||
environment: ""
|
||||
want_undefined: true
|
||||
|
||||
- note: pass_rg_tag_missing
|
||||
resource:
|
||||
type: "Microsoft.Compute/virtualMachines"
|
||||
name: "vm-rg-no-tag"
|
||||
properties:
|
||||
tags:
|
||||
environment: "dev"
|
||||
parameters:
|
||||
tagName: "environment"
|
||||
context:
|
||||
resourceGroup:
|
||||
tags: {}
|
||||
want_undefined: true
|
||||
861
tests/azure_policy/cases/e2e_vm_backup_dine.yaml
Normal file
861
tests/azure_policy/cases/e2e_vm_backup_dine.yaml
Normal file
@@ -0,0 +1,861 @@
|
||||
# Copyright (c) Microsoft Corporation.
|
||||
# Licensed under the MIT License.
|
||||
|
||||
# E2E Test: Backup/VirtualMachineBackup_DINE
|
||||
# Real Azure Policy: "Configure backup on virtual machines without a given tag
|
||||
# to an existing recovery services vault in the same location"
|
||||
# Source: regolator/policyDefinitions/Backup/VirtualMachineBackup_DINE.json
|
||||
#
|
||||
# Features exercised:
|
||||
# - DeployIfNotExists effect
|
||||
# - concat('tags[', parameters('exclusionTagName'), ']') — dynamic tag field access
|
||||
# - empty() function for Boolean branching
|
||||
# - notEquals for image publisher exclusion (azureopenshift, AzureDatabricks)
|
||||
# - notContains on resource id (databricks-rg-)
|
||||
# - Large anyOf/allOf for image publisher/offer/SKU matching
|
||||
# - host_await for cross-resource DINE lookup (backupprotecteditems)
|
||||
# - Deployment template with nested deployment
|
||||
|
||||
aliases: test_aliases.json
|
||||
|
||||
policy_definition: |
|
||||
{
|
||||
"properties": {
|
||||
"displayName": "Configure backup on virtual machines without a given tag to an existing recovery services vault in the same location",
|
||||
"policyType": "BuiltIn",
|
||||
"mode": "Indexed",
|
||||
"parameters": {
|
||||
"vaultLocation": {
|
||||
"type": "String"
|
||||
},
|
||||
"backupPolicyId": {
|
||||
"type": "String"
|
||||
},
|
||||
"exclusionTagName": {
|
||||
"type": "String",
|
||||
"defaultValue": ""
|
||||
},
|
||||
"exclusionTagValue": {
|
||||
"type": "Array",
|
||||
"defaultValue": []
|
||||
},
|
||||
"effect": {
|
||||
"type": "String",
|
||||
"allowedValues": [
|
||||
"auditIfNotExists",
|
||||
"AuditIfNotExists",
|
||||
"deployIfNotExists",
|
||||
"DeployIfNotExists",
|
||||
"disabled",
|
||||
"Disabled"
|
||||
],
|
||||
"defaultValue": "DeployIfNotExists"
|
||||
}
|
||||
},
|
||||
"policyRule": {
|
||||
"if": {
|
||||
"allOf": [
|
||||
{
|
||||
"field": "type",
|
||||
"equals": "Microsoft.Compute/virtualMachines"
|
||||
},
|
||||
{
|
||||
"field": "location",
|
||||
"equals": "[parameters('vaultLocation')]"
|
||||
},
|
||||
{
|
||||
"field": "id",
|
||||
"notContains": "/resourceGroups/databricks-rg-"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.Compute/imagePublisher",
|
||||
"notEquals": "azureopenshift"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.Compute/imagePublisher",
|
||||
"notEquals": "AzureDatabricks"
|
||||
},
|
||||
{
|
||||
"anyOf": [
|
||||
{
|
||||
"not": {
|
||||
"field": "[concat('tags[', parameters('exclusionTagName'), ']')]",
|
||||
"in": "[parameters('exclusionTagValue')]"
|
||||
}
|
||||
},
|
||||
{
|
||||
"value": "[empty(parameters('exclusionTagValue'))]",
|
||||
"equals": "true"
|
||||
},
|
||||
{
|
||||
"value": "[empty(parameters('exclusionTagName'))]",
|
||||
"equals": "true"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"anyOf": [
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"field": "Microsoft.Compute/imagePublisher",
|
||||
"equals": "MicrosoftWindowsServer"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.Compute/imageOffer",
|
||||
"equals": "WindowsServer"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.Compute/imageSKU",
|
||||
"in": [
|
||||
"2008-R2-SP1",
|
||||
"2008-R2-SP1-smalldisk",
|
||||
"2012-Datacenter",
|
||||
"2012-Datacenter-smalldisk",
|
||||
"2012-R2-Datacenter",
|
||||
"2012-R2-Datacenter-smalldisk",
|
||||
"2016-Datacenter",
|
||||
"2016-datacenter-gensecond",
|
||||
"2016-Datacenter-Server-Core",
|
||||
"2016-Datacenter-Server-Core-smalldisk",
|
||||
"2016-Datacenter-smalldisk",
|
||||
"2016-Datacenter-with-Containers",
|
||||
"2016-Datacenter-with-RDSH",
|
||||
"2019-Datacenter",
|
||||
"2019-Datacenter-Core",
|
||||
"2019-Datacenter-Core-smalldisk",
|
||||
"2019-Datacenter-Core-with-Containers",
|
||||
"2019-Datacenter-Core-with-Containers-smalldisk",
|
||||
"2019-Datacenter-smalldisk",
|
||||
"2019-Datacenter-with-Containers",
|
||||
"2019-Datacenter-with-Containers-smalldisk",
|
||||
"2019-Datacenter-zhcn",
|
||||
"2019-datacenter-gensecond",
|
||||
"2022-datacenter-g2",
|
||||
"2022-datacenter",
|
||||
"2022-datacenter-azure-edition",
|
||||
"2022-datacenter-azure-edition-smalldisk",
|
||||
"2022-datacenter-azure-edition-core",
|
||||
"2022-datacenter-azure-edition-core-smalldisk",
|
||||
"2022-datacenter-smalldisk-g2",
|
||||
"2022-datacenter-smalldisk",
|
||||
"2022-datacenter-core-g2",
|
||||
"2022-datacenter-core",
|
||||
"2022-datacenter-core-smalldisk-g2",
|
||||
"2022-datacenter-core-smalldisk"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"field": "Microsoft.Compute/imagePublisher",
|
||||
"equals": "MicrosoftWindowsServer"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.Compute/imageOffer",
|
||||
"equals": "WindowsServerSemiAnnual"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.Compute/imageSKU",
|
||||
"in": [
|
||||
"Datacenter-Core-1709-smalldisk",
|
||||
"Datacenter-Core-1709-with-Containers-smalldisk",
|
||||
"Datacenter-Core-1803-with-Containers-smalldisk"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"field": "Microsoft.Compute/imagePublisher",
|
||||
"equals": "MicrosoftWindowsServerHPCPack"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.Compute/imageOffer",
|
||||
"equals": "WindowsServerHPCPack"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"field": "Microsoft.Compute/imagePublisher",
|
||||
"equals": "MicrosoftSQLServer"
|
||||
},
|
||||
{
|
||||
"anyOf": [
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"field": "Microsoft.Compute/imageOffer",
|
||||
"like": "sql2019-ws2022"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.Compute/imageSKU",
|
||||
"in": [
|
||||
"sqldev",
|
||||
"sqldev-gen2",
|
||||
"standard",
|
||||
"standard-gen2"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"anyOf": [
|
||||
{
|
||||
"field": "Microsoft.Compute/imageOffer",
|
||||
"like": "*-WS2019"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.Compute/imageOffer",
|
||||
"like": "*-WS2016"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.Compute/imageOffer",
|
||||
"like": "*-WS2016-BYOL"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.Compute/imageOffer",
|
||||
"like": "*-WS2012R2"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.Compute/imageOffer",
|
||||
"like": "*-WS2012R2-BYOL"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"field": "Microsoft.Compute/imagePublisher",
|
||||
"equals": "MicrosoftRServer"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.Compute/imageOffer",
|
||||
"equals": "MLServer-WS2016"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"field": "Microsoft.Compute/imagePublisher",
|
||||
"equals": "MicrosoftVisualStudio"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.Compute/imageOffer",
|
||||
"in": [
|
||||
"VisualStudio",
|
||||
"Windows"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"field": "Microsoft.Compute/imagePublisher",
|
||||
"equals": "MicrosoftDynamicsAX"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.Compute/imageOffer",
|
||||
"equals": "Dynamics"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.Compute/imageSKU",
|
||||
"equals": "Pre-Req-AX7-Onebox-U8"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"field": "Microsoft.Compute/imagePublisher",
|
||||
"equals": "microsoft-ads"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.Compute/imageOffer",
|
||||
"equals": "windows-data-science-vm"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"field": "Microsoft.Compute/imagePublisher",
|
||||
"equals": "MicrosoftWindowsDesktop"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.Compute/imageOffer",
|
||||
"equals": "Windows-10"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"field": "Microsoft.Compute/imagePublisher",
|
||||
"equals": "MicrosoftWindowsDesktop"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.Compute/imageOffer",
|
||||
"equals": "Windows-11"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"field": "Microsoft.Compute/imagePublisher",
|
||||
"equals": "RedHat"
|
||||
},
|
||||
{
|
||||
"anyOf": [
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"field": "Microsoft.Compute/imageOffer",
|
||||
"equals": "RHEL-BYOS"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.Compute/imageSKU",
|
||||
"equals": "rhel-lvm77"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"field": "Microsoft.Compute/imageOffer",
|
||||
"in": [
|
||||
"RHEL",
|
||||
"RHEL-SAP-HANA"
|
||||
]
|
||||
},
|
||||
{
|
||||
"anyOf": [
|
||||
{
|
||||
"field": "Microsoft.Compute/imageSKU",
|
||||
"like": "6.*"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.Compute/imageSKU",
|
||||
"like": "7*"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.Compute/imageSKU",
|
||||
"like": "8*"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"field": "Microsoft.Compute/imagePublisher",
|
||||
"equals": "SUSE"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.Compute/imageOffer",
|
||||
"in": [
|
||||
"SLES",
|
||||
"SLES-HPC",
|
||||
"SLES-HPC-Priority",
|
||||
"SLES-SAP",
|
||||
"SLES-SAP-BYOS",
|
||||
"SLES-Priority",
|
||||
"SLES-BYOS",
|
||||
"SLES-SAPCAL",
|
||||
"SLES-Standard"
|
||||
]
|
||||
},
|
||||
{
|
||||
"anyOf": [
|
||||
{
|
||||
"field": "Microsoft.Compute/imageSKU",
|
||||
"like": "12*"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"field": "Microsoft.Compute/imagePublisher",
|
||||
"equals": "Canonical"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.Compute/imageOffer",
|
||||
"contains": "ubuntu"
|
||||
},
|
||||
{
|
||||
"anyOf": [
|
||||
{
|
||||
"field": "Microsoft.Compute/imageSKU",
|
||||
"like": "14.04*LTS"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.Compute/imageSKU",
|
||||
"like": "16.04*LTS"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.Compute/imageSKU",
|
||||
"like": "18.04*LTS"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.Compute/imageSKU",
|
||||
"like": "*20_04-lts"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.Compute/imageSKU",
|
||||
"like": "20_04-lts*"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.Compute/imageSKU",
|
||||
"like": "22_04-lts-gen2"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"field": "Microsoft.Compute/imagePublisher",
|
||||
"equals": "Oracle"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.Compute/imageOffer",
|
||||
"equals": "Oracle-Linux"
|
||||
},
|
||||
{
|
||||
"anyOf": [
|
||||
{
|
||||
"field": "Microsoft.Compute/imageSKU",
|
||||
"like": "6.*"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.Compute/imageSKU",
|
||||
"like": "7*"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"field": "Microsoft.Compute/imagePublisher",
|
||||
"equals": "OpenLogic"
|
||||
},
|
||||
{
|
||||
"anyOf": [
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"field": "Microsoft.Compute/imageOffer",
|
||||
"equals": "CentOS-HPC"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.Compute/imageSKU",
|
||||
"equals": "7_9-gen2"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"field": "Microsoft.Compute/imageOffer",
|
||||
"in": [
|
||||
"CentOS",
|
||||
"Centos-LVM",
|
||||
"CentOS-SRIOV"
|
||||
]
|
||||
},
|
||||
{
|
||||
"anyOf": [
|
||||
{
|
||||
"field": "Microsoft.Compute/imageSKU",
|
||||
"like": "6.*"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.Compute/imageSKU",
|
||||
"like": "7*"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"field": "Microsoft.Compute/imagePublisher",
|
||||
"equals": "cloudera"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.Compute/imageOffer",
|
||||
"equals": "cloudera-centos-os"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.Compute/imageSKU",
|
||||
"like": "7*"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"then": {
|
||||
"effect": "[parameters('effect')]",
|
||||
"details": {
|
||||
"roleDefinitionIds": [
|
||||
"/providers/microsoft.authorization/roleDefinitions/9980e02c-c2be-4d73-94e8-173b1dc7cf3c",
|
||||
"/providers/microsoft.authorization/roleDefinitions/5e467623-bb1f-42f4-a55d-6e525e11384b"
|
||||
],
|
||||
"type": "Microsoft.RecoveryServices/backupprotecteditems",
|
||||
"deployment": {
|
||||
"properties": {
|
||||
"mode": "incremental",
|
||||
"template": {
|
||||
"$schema": "http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
|
||||
"contentVersion": "1.0.0.0",
|
||||
"parameters": {
|
||||
"backupPolicyId": {
|
||||
"type": "String"
|
||||
},
|
||||
"fabricName": {
|
||||
"type": "String"
|
||||
},
|
||||
"protectionContainers": {
|
||||
"type": "String"
|
||||
},
|
||||
"protectedItems": {
|
||||
"type": "String"
|
||||
},
|
||||
"sourceResourceId": {
|
||||
"type": "String"
|
||||
}
|
||||
},
|
||||
"resources": [
|
||||
{
|
||||
"apiVersion": "2017-05-10",
|
||||
"name": "[concat('DeployProtection-',uniqueString(parameters('protectedItems')))]",
|
||||
"type": "Microsoft.Resources/deployments",
|
||||
"resourceGroup": "[first(skip(split(parameters('backupPolicyId'), '/'), 4))]",
|
||||
"subscriptionId": "[first(skip(split(parameters('backupPolicyId'), '/'), 2))]",
|
||||
"properties": {
|
||||
"mode": "Incremental",
|
||||
"template": {
|
||||
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
|
||||
"contentVersion": "1.0.0.0",
|
||||
"parameters": {
|
||||
"backupPolicyId": {
|
||||
"type": "String"
|
||||
},
|
||||
"fabricName": {
|
||||
"type": "String"
|
||||
},
|
||||
"protectionContainers": {
|
||||
"type": "String"
|
||||
},
|
||||
"protectedItems": {
|
||||
"type": "String"
|
||||
},
|
||||
"sourceResourceId": {
|
||||
"type": "String"
|
||||
}
|
||||
},
|
||||
"resources": [
|
||||
{
|
||||
"type": "Microsoft.RecoveryServices/vaults/backupFabrics/protectionContainers/protectedItems",
|
||||
"name": "[concat(first(skip(split(parameters('backupPolicyId'), '/'), 8)), '/', parameters('fabricName'), '/',parameters('protectionContainers'), '/', parameters('protectedItems'))]",
|
||||
"apiVersion": "2016-06-01",
|
||||
"properties": {
|
||||
"protectedItemType": "Microsoft.Compute/virtualMachines",
|
||||
"policyId": "[parameters('backupPolicyId')]",
|
||||
"sourceResourceId": "[parameters('sourceResourceId')]"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"parameters": {
|
||||
"backupPolicyId": {
|
||||
"value": "[parameters('backupPolicyId')]"
|
||||
},
|
||||
"fabricName": {
|
||||
"value": "[parameters('fabricName')]"
|
||||
},
|
||||
"protectionContainers": {
|
||||
"value": "[parameters('protectionContainers')]"
|
||||
},
|
||||
"protectedItems": {
|
||||
"value": "[parameters('protectedItems')]"
|
||||
},
|
||||
"sourceResourceId": {
|
||||
"value": "[parameters('sourceResourceId')]"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"parameters": {
|
||||
"backupPolicyId": {
|
||||
"value": "[parameters('backupPolicyId')]"
|
||||
},
|
||||
"fabricName": {
|
||||
"value": "Azure"
|
||||
},
|
||||
"protectionContainers": {
|
||||
"value": "[concat('iaasvmcontainer;iaasvmcontainerv2;', resourceGroup().name, ';' ,field('name'))]"
|
||||
},
|
||||
"protectedItems": {
|
||||
"value": "[concat('vm;iaasvmcontainerv2;', resourceGroup().name, ';' ,field('name'))]"
|
||||
},
|
||||
"sourceResourceId": {
|
||||
"value": "[concat('/subscriptions/', subscription().subscriptionId, '/resourceGroups/', resourceGroup().name, '/providers/Microsoft.Compute/virtualMachines/',field('name'))]"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cases:
|
||||
# =========================================================================
|
||||
# VM without backup → no related backupprotecteditems → DeployIfNotExists
|
||||
# (default empty exclusion tag params → empty() branches pass the anyOf)
|
||||
# =========================================================================
|
||||
|
||||
- note: dine_vm_no_backup
|
||||
resource:
|
||||
type: "Microsoft.Compute/virtualMachines"
|
||||
name: "vm-no-backup"
|
||||
location: "eastus"
|
||||
properties:
|
||||
storageProfile:
|
||||
imageReference:
|
||||
publisher: "MicrosoftWindowsServer"
|
||||
offer: "WindowsServer"
|
||||
sku: "2019-Datacenter"
|
||||
parameters:
|
||||
vaultLocation: "eastus"
|
||||
backupPolicyId: "/subscriptions/sub1/resourceGroups/backup-rg/providers/Microsoft.RecoveryServices/vaults/vault1/backupPolicies/DefaultPolicy"
|
||||
host_await:
|
||||
- key:
|
||||
operation: "lookup_related_resources"
|
||||
type: "Microsoft.RecoveryServices/backupprotecteditems"
|
||||
response: null
|
||||
want_effect: "DeployIfNotExists"
|
||||
want_details:
|
||||
roleDefinitionIds:
|
||||
- "/providers/microsoft.authorization/roleDefinitions/9980e02c-c2be-4d73-94e8-173b1dc7cf3c"
|
||||
- "/providers/microsoft.authorization/roleDefinitions/5e467623-bb1f-42f4-a55d-6e525e11384b"
|
||||
type: "Microsoft.RecoveryServices/backupprotecteditems"
|
||||
|
||||
# =========================================================================
|
||||
# VM with existing backup → backupprotecteditems found → compliant (pass)
|
||||
# =========================================================================
|
||||
|
||||
- note: pass_vm_with_backup
|
||||
resource:
|
||||
type: "Microsoft.Compute/virtualMachines"
|
||||
name: "vm-has-backup"
|
||||
location: "eastus"
|
||||
properties:
|
||||
storageProfile:
|
||||
imageReference:
|
||||
publisher: "MicrosoftWindowsServer"
|
||||
offer: "WindowsServer"
|
||||
sku: "2019-Datacenter"
|
||||
parameters:
|
||||
vaultLocation: "eastus"
|
||||
backupPolicyId: "/subscriptions/sub1/resourceGroups/backup-rg/providers/Microsoft.RecoveryServices/vaults/vault1/backupPolicies/DefaultPolicy"
|
||||
host_await:
|
||||
- response:
|
||||
properties:
|
||||
protectedItemType: "Microsoft.Compute/virtualMachines"
|
||||
policyId: "/subscriptions/sub1/resourceGroups/backup-rg/providers/Microsoft.RecoveryServices/vaults/vault1/backupPolicies/DefaultPolicy"
|
||||
sourceResourceId: "/subscriptions/sub1/resourceGroups/rg1/providers/Microsoft.Compute/virtualMachines/vm-has-backup"
|
||||
want_undefined: true
|
||||
|
||||
# =========================================================================
|
||||
# VM with exclusion tag matching parameter value → excluded from scope → pass
|
||||
# Tag "backup-opt-out" = "true" is IN exclusionTagValue ["true", "yes"]
|
||||
# → not(in) = false, empty(tagValue) = false, empty(tagName) = false
|
||||
# → anyOf fails → allOf fails → if-condition false → pass
|
||||
# =========================================================================
|
||||
|
||||
- note: pass_vm_excluded_by_tag
|
||||
resource:
|
||||
type: "Microsoft.Compute/virtualMachines"
|
||||
name: "vm-excluded-tag"
|
||||
location: "eastus"
|
||||
tags:
|
||||
backup-opt-out: "true"
|
||||
properties:
|
||||
storageProfile:
|
||||
imageReference:
|
||||
publisher: "MicrosoftWindowsServer"
|
||||
offer: "WindowsServer"
|
||||
sku: "2019-Datacenter"
|
||||
parameters:
|
||||
vaultLocation: "eastus"
|
||||
backupPolicyId: "/subscriptions/sub1/resourceGroups/backup-rg/providers/Microsoft.RecoveryServices/vaults/vault1/backupPolicies/DefaultPolicy"
|
||||
exclusionTagName: "backup-opt-out"
|
||||
exclusionTagValue:
|
||||
- "true"
|
||||
- "yes"
|
||||
want_undefined: true
|
||||
|
||||
# =========================================================================
|
||||
# VM with excluded publisher "AzureDatabricks" → notEquals fails → pass
|
||||
# =========================================================================
|
||||
|
||||
- note: pass_vm_excluded_publisher
|
||||
resource:
|
||||
type: "Microsoft.Compute/virtualMachines"
|
||||
name: "vm-databricks"
|
||||
location: "eastus"
|
||||
properties:
|
||||
storageProfile:
|
||||
imageReference:
|
||||
publisher: "AzureDatabricks"
|
||||
offer: "databricks"
|
||||
sku: "standard"
|
||||
parameters:
|
||||
vaultLocation: "eastus"
|
||||
backupPolicyId: "/subscriptions/sub1/resourceGroups/backup-rg/providers/Microsoft.RecoveryServices/vaults/vault1/backupPolicies/DefaultPolicy"
|
||||
want_undefined: true
|
||||
|
||||
# =========================================================================
|
||||
# Wrong resource type → type check fails → pass
|
||||
# =========================================================================
|
||||
|
||||
- note: pass_wrong_type
|
||||
resource:
|
||||
type: "Microsoft.Storage/storageAccounts"
|
||||
name: "storage1"
|
||||
location: "eastus"
|
||||
properties: {}
|
||||
parameters:
|
||||
vaultLocation: "eastus"
|
||||
backupPolicyId: "/subscriptions/sub1/resourceGroups/backup-rg/providers/Microsoft.RecoveryServices/vaults/vault1/backupPolicies/DefaultPolicy"
|
||||
want_undefined: true
|
||||
|
||||
# =========================================================================
|
||||
# VM has exclusion tag but value NOT in exclusion list → still in scope
|
||||
# Tag "backup-opt-out" = "no" is NOT IN ["true", "yes"]
|
||||
# → not(in) = true → anyOf passes → VM is in scope → DINE
|
||||
# =========================================================================
|
||||
|
||||
- note: dine_vm_tag_not_in_exclusion
|
||||
resource:
|
||||
type: "Microsoft.Compute/virtualMachines"
|
||||
name: "vm-tag-no-match"
|
||||
location: "eastus"
|
||||
tags:
|
||||
backup-opt-out: "no"
|
||||
properties:
|
||||
storageProfile:
|
||||
imageReference:
|
||||
publisher: "MicrosoftWindowsServer"
|
||||
offer: "WindowsServer"
|
||||
sku: "2019-Datacenter"
|
||||
parameters:
|
||||
vaultLocation: "eastus"
|
||||
backupPolicyId: "/subscriptions/sub1/resourceGroups/backup-rg/providers/Microsoft.RecoveryServices/vaults/vault1/backupPolicies/DefaultPolicy"
|
||||
exclusionTagName: "backup-opt-out"
|
||||
exclusionTagValue:
|
||||
- "true"
|
||||
- "yes"
|
||||
host_await:
|
||||
- key:
|
||||
operation: "lookup_related_resources"
|
||||
type: "Microsoft.RecoveryServices/backupprotecteditems"
|
||||
response: null
|
||||
want_effect: "DeployIfNotExists"
|
||||
|
||||
# =========================================================================
|
||||
# VM in a different location than vaultLocation → location check fails → pass
|
||||
# =========================================================================
|
||||
|
||||
- note: pass_vm_wrong_location
|
||||
resource:
|
||||
type: "Microsoft.Compute/virtualMachines"
|
||||
name: "vm-wrong-loc"
|
||||
location: "westeurope"
|
||||
properties:
|
||||
storageProfile:
|
||||
imageReference:
|
||||
publisher: "MicrosoftWindowsServer"
|
||||
offer: "WindowsServer"
|
||||
sku: "2019-Datacenter"
|
||||
parameters:
|
||||
vaultLocation: "eastus"
|
||||
backupPolicyId: "/subscriptions/sub1/resourceGroups/backup-rg/providers/Microsoft.RecoveryServices/vaults/vault1/backupPolicies/DefaultPolicy"
|
||||
want_undefined: true
|
||||
|
||||
# =========================================================================
|
||||
# VM in a Databricks resource group → notContains "/resourceGroups/databricks-rg-"
|
||||
# fails → allOf fails → pass
|
||||
# =========================================================================
|
||||
|
||||
- note: pass_vm_databricks_rg
|
||||
resource:
|
||||
type: "Microsoft.Compute/virtualMachines"
|
||||
name: "vm-dbr"
|
||||
id: "/subscriptions/sub1/resourceGroups/databricks-rg-myworkspace/providers/Microsoft.Compute/virtualMachines/vm-dbr"
|
||||
location: "eastus"
|
||||
properties:
|
||||
storageProfile:
|
||||
imageReference:
|
||||
publisher: "MicrosoftWindowsServer"
|
||||
offer: "WindowsServer"
|
||||
sku: "2019-Datacenter"
|
||||
parameters:
|
||||
vaultLocation: "eastus"
|
||||
backupPolicyId: "/subscriptions/sub1/resourceGroups/backup-rg/providers/Microsoft.RecoveryServices/vaults/vault1/backupPolicies/DefaultPolicy"
|
||||
want_undefined: true
|
||||
|
||||
# =========================================================================
|
||||
# VM with publisher "azureopenshift" → notEquals fails → allOf fails → pass
|
||||
# =========================================================================
|
||||
|
||||
- note: pass_vm_excluded_publisher_azureopenshift
|
||||
resource:
|
||||
type: "Microsoft.Compute/virtualMachines"
|
||||
name: "vm-openshift"
|
||||
location: "eastus"
|
||||
properties:
|
||||
storageProfile:
|
||||
imageReference:
|
||||
publisher: "azureopenshift"
|
||||
offer: "aro4"
|
||||
sku: "aro_worker"
|
||||
parameters:
|
||||
vaultLocation: "eastus"
|
||||
backupPolicyId: "/subscriptions/sub1/resourceGroups/backup-rg/providers/Microsoft.RecoveryServices/vaults/vault1/backupPolicies/DefaultPolicy"
|
||||
want_undefined: true
|
||||
156
tests/azure_policy/cases/e2e_vm_managed_disk.yaml
Normal file
156
tests/azure_policy/cases/e2e_vm_managed_disk.yaml
Normal file
@@ -0,0 +1,156 @@
|
||||
# Copyright (c) Microsoft Corporation.
|
||||
# Licensed under the MIT License.
|
||||
|
||||
# E2E Test: Compute/VMRequireManagedDisk_Audit
|
||||
# Real Azure Policy: "Audit VMs that do not use managed disks"
|
||||
# Features: anyOf, allOf nesting, field (type + alias), equals, exists,
|
||||
# multiple resource types (VM + VMSS)
|
||||
|
||||
aliases: test_aliases.json
|
||||
|
||||
policy_definition: |
|
||||
{
|
||||
"properties": {
|
||||
"displayName": "Audit VMs that do not use managed disks",
|
||||
"policyType": "BuiltIn",
|
||||
"mode": "All",
|
||||
"parameters": {},
|
||||
"policyRule": {
|
||||
"if": {
|
||||
"anyOf": [
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"field": "type",
|
||||
"equals": "Microsoft.Compute/virtualMachines"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.Compute/virtualMachines/osDisk.uri",
|
||||
"exists": "True"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"field": "type",
|
||||
"equals": "Microsoft.Compute/VirtualMachineScaleSets"
|
||||
},
|
||||
{
|
||||
"anyOf": [
|
||||
{
|
||||
"field": "Microsoft.Compute/VirtualMachineScaleSets/osDisk.vhdContainers",
|
||||
"exists": "True"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.Compute/VirtualMachineScaleSets/osdisk.imageUrl",
|
||||
"exists": "True"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"then": {
|
||||
"effect": "audit"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cases:
|
||||
# =========================================================================
|
||||
# Audit — VM with unmanaged OS disk (vhd uri present)
|
||||
# =========================================================================
|
||||
|
||||
- note: audit_vm_unmanaged_osdisk
|
||||
resource:
|
||||
type: "Microsoft.Compute/virtualMachines"
|
||||
name: "legacyVM"
|
||||
location: "eastus"
|
||||
properties:
|
||||
storageProfile:
|
||||
osDisk:
|
||||
vhd:
|
||||
uri: "https://mystorage.blob.core.windows.net/vhds/osdisk.vhd"
|
||||
want_effect: "audit"
|
||||
|
||||
# =========================================================================
|
||||
# No effect — VM with managed disk (no vhd uri)
|
||||
# =========================================================================
|
||||
|
||||
- note: pass_vm_managed_disk
|
||||
resource:
|
||||
type: "Microsoft.Compute/virtualMachines"
|
||||
name: "modernVM"
|
||||
location: "eastus"
|
||||
properties:
|
||||
storageProfile:
|
||||
osDisk:
|
||||
managedDisk:
|
||||
storageAccountType: "Premium_LRS"
|
||||
want_undefined: true
|
||||
|
||||
# =========================================================================
|
||||
# Audit — VMSS with vhdContainers (unmanaged)
|
||||
# =========================================================================
|
||||
|
||||
- note: audit_vmss_vhd_containers
|
||||
resource:
|
||||
type: "Microsoft.Compute/VirtualMachineScaleSets"
|
||||
name: "legacyScaleSet"
|
||||
location: "westus"
|
||||
properties:
|
||||
virtualMachineProfile:
|
||||
storageProfile:
|
||||
osDisk:
|
||||
vhdContainers:
|
||||
- "https://stor1.blob.core.windows.net/vhds"
|
||||
want_effect: "audit"
|
||||
|
||||
# =========================================================================
|
||||
# Audit — VMSS with custom image URL (unmanaged)
|
||||
# =========================================================================
|
||||
|
||||
- note: audit_vmss_image_url
|
||||
resource:
|
||||
type: "Microsoft.Compute/VirtualMachineScaleSets"
|
||||
name: "customImgScaleSet"
|
||||
location: "westus"
|
||||
properties:
|
||||
virtualMachineProfile:
|
||||
storageProfile:
|
||||
osDisk:
|
||||
image:
|
||||
uri: "https://mystorage.blob.core.windows.net/images/custom.vhd"
|
||||
want_effect: "audit"
|
||||
|
||||
# =========================================================================
|
||||
# No effect — VMSS with managed disk (no vhdContainers, no imageUrl)
|
||||
# =========================================================================
|
||||
|
||||
- note: pass_vmss_managed_disk
|
||||
resource:
|
||||
type: "Microsoft.Compute/VirtualMachineScaleSets"
|
||||
name: "modernScaleSet"
|
||||
location: "eastus"
|
||||
properties:
|
||||
virtualMachineProfile:
|
||||
storageProfile:
|
||||
osDisk:
|
||||
managedDisk:
|
||||
storageAccountType: "Standard_LRS"
|
||||
want_undefined: true
|
||||
|
||||
# =========================================================================
|
||||
# No effect — wrong resource type entirely
|
||||
# =========================================================================
|
||||
|
||||
- note: skip_wrong_type
|
||||
resource:
|
||||
type: "Microsoft.Storage/storageAccounts"
|
||||
name: "myStorage"
|
||||
location: "eastus"
|
||||
properties: {}
|
||||
want_undefined: true
|
||||
342
tests/azure_policy/cases/e2e_vm_replication_dine.yaml
Normal file
342
tests/azure_policy/cases/e2e_vm_replication_dine.yaml
Normal file
@@ -0,0 +1,342 @@
|
||||
# Copyright (c) Microsoft Corporation.
|
||||
# Licensed under the MIT License.
|
||||
|
||||
# E2E Test: Compute/VirtualMachineReplication_AzureSiteRecovery_DINE
|
||||
# Real Azure Policy: "Configure disaster recovery on virtual machines by
|
||||
# enabling replication via Azure Site Recovery"
|
||||
# Source: regolator/policyDefinitions/Compute/VirtualMachineReplication_AzureSiteRecovery_DINE.json
|
||||
#
|
||||
# Features exercised:
|
||||
# - DeployIfNotExists effect
|
||||
# - concat('tags[', parameters('tagName'), ']') — dynamic tag field access
|
||||
# - empty() function for Boolean branching
|
||||
# - in / notIn operators on tag values
|
||||
# - Multi-branch anyOf/allOf with parameterised control flow
|
||||
# - existenceCondition with like + contains operators
|
||||
# - host_await for cross-resource lookup
|
||||
|
||||
aliases: test_aliases.json
|
||||
|
||||
policy_definition: |
|
||||
{
|
||||
"properties": {
|
||||
"displayName": "Configure disaster recovery on virtual machines",
|
||||
"policyType": "BuiltIn",
|
||||
"mode": "Indexed",
|
||||
"parameters": {
|
||||
"sourceRegion": {
|
||||
"type": "String"
|
||||
},
|
||||
"targetRegion": {
|
||||
"type": "String"
|
||||
},
|
||||
"targetResourceGroupId": {
|
||||
"type": "String"
|
||||
},
|
||||
"vaultResourceGroupId": {
|
||||
"type": "String"
|
||||
},
|
||||
"vaultId": {
|
||||
"type": "String"
|
||||
},
|
||||
"recoveryNetworkId": {
|
||||
"type": "String",
|
||||
"defaultValue": ""
|
||||
},
|
||||
"targetZone": {
|
||||
"type": "String",
|
||||
"defaultValue": ""
|
||||
},
|
||||
"cacheStorageAccountId": {
|
||||
"type": "String",
|
||||
"defaultValue": ""
|
||||
},
|
||||
"tagName": {
|
||||
"type": "String",
|
||||
"defaultValue": ""
|
||||
},
|
||||
"tagValue": {
|
||||
"type": "Array",
|
||||
"defaultValue": []
|
||||
},
|
||||
"tagType": {
|
||||
"type": "String",
|
||||
"allowedValues": ["Inclusion", "Exclusion", ""],
|
||||
"defaultValue": ""
|
||||
},
|
||||
"effect": {
|
||||
"type": "String",
|
||||
"allowedValues": ["DeployIfNotExists", "Disabled"],
|
||||
"defaultValue": "DeployIfNotExists"
|
||||
}
|
||||
},
|
||||
"policyRule": {
|
||||
"if": {
|
||||
"allOf": [
|
||||
{
|
||||
"field": "type",
|
||||
"equals": "Microsoft.Compute/virtualMachines"
|
||||
},
|
||||
{
|
||||
"field": "location",
|
||||
"equals": "[parameters('sourceRegion')]"
|
||||
},
|
||||
{
|
||||
"anyOf": [
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"value": "[parameters('tagType')]",
|
||||
"equals": "Inclusion"
|
||||
},
|
||||
{
|
||||
"field": "[concat('tags[', parameters('tagName'), ']')]",
|
||||
"in": "[parameters('tagValue')]"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"value": "[parameters('tagType')]",
|
||||
"equals": "Exclusion"
|
||||
},
|
||||
{
|
||||
"field": "[concat('tags[', parameters('tagName'), ']')]",
|
||||
"notIn": "[parameters('tagValue')]"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"anyOf": [
|
||||
{
|
||||
"value": "[empty(parameters('tagName'))]",
|
||||
"equals": "true"
|
||||
},
|
||||
{
|
||||
"value": "[empty(parameters('tagValue'))]",
|
||||
"equals": "true"
|
||||
},
|
||||
{
|
||||
"value": "[empty(parameters('tagType'))]",
|
||||
"equals": "true"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"then": {
|
||||
"effect": "[parameters('effect')]",
|
||||
"details": {
|
||||
"type": "Microsoft.Resources/links",
|
||||
"existenceCondition": {
|
||||
"allOf": [
|
||||
{
|
||||
"field": "name",
|
||||
"like": "ASR-Policy-Protect-*"
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.Resources/links/targetId",
|
||||
"contains": "/replicationProtectedItems/"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cases:
|
||||
# =========================================================================
|
||||
# No tag filters (all params empty) → matches, related resource not found → DINE
|
||||
# =========================================================================
|
||||
|
||||
- note: dine_no_tag_filter_no_related
|
||||
resource:
|
||||
type: "Microsoft.Compute/virtualMachines"
|
||||
name: "vm-no-dr"
|
||||
location: "eastus"
|
||||
properties: {}
|
||||
parameters:
|
||||
sourceRegion: "eastus"
|
||||
targetRegion: "westus"
|
||||
targetResourceGroupId: "/subscriptions/sub1/resourceGroups/rg-dr"
|
||||
vaultResourceGroupId: "/subscriptions/sub1/resourceGroups/rg-vault"
|
||||
vaultId: "/subscriptions/sub1/resourceGroups/rg-vault/providers/Microsoft.RecoveryServices/vaults/vault1"
|
||||
host_await:
|
||||
- key:
|
||||
operation: "lookup_related_resources"
|
||||
type: "Microsoft.Resources/links"
|
||||
response: null
|
||||
want_effect: "DeployIfNotExists"
|
||||
|
||||
# =========================================================================
|
||||
# No tag filter, related resource found with matching name/targetId → compliant
|
||||
# =========================================================================
|
||||
|
||||
- note: compliant_dr_configured
|
||||
resource:
|
||||
type: "Microsoft.Compute/virtualMachines"
|
||||
name: "vm-dr-ok"
|
||||
location: "eastus"
|
||||
properties: {}
|
||||
parameters:
|
||||
sourceRegion: "eastus"
|
||||
targetRegion: "westus"
|
||||
targetResourceGroupId: "/subscriptions/sub1/resourceGroups/rg-dr"
|
||||
vaultResourceGroupId: "/subscriptions/sub1/resourceGroups/rg-vault"
|
||||
vaultId: "/subscriptions/sub1/resourceGroups/rg-vault/providers/Microsoft.RecoveryServices/vaults/vault1"
|
||||
host_await:
|
||||
- response:
|
||||
name: "ASR-Policy-Protect-vm-dr-ok"
|
||||
properties:
|
||||
targetid: "/subscriptions/sub1/resourceGroups/rg-vault/providers/Microsoft.RecoveryServices/vaults/vault1/replicationProtectedItems/item1"
|
||||
want_undefined: true
|
||||
|
||||
# =========================================================================
|
||||
# Related resource found but name doesn't match like → DINE
|
||||
# =========================================================================
|
||||
|
||||
- note: dine_wrong_link_name
|
||||
resource:
|
||||
type: "Microsoft.Compute/virtualMachines"
|
||||
name: "vm-wrong-link"
|
||||
location: "eastus"
|
||||
properties: {}
|
||||
parameters:
|
||||
sourceRegion: "eastus"
|
||||
targetRegion: "westus"
|
||||
targetResourceGroupId: "/subscriptions/sub1/resourceGroups/rg-dr"
|
||||
vaultResourceGroupId: "/subscriptions/sub1/resourceGroups/rg-vault"
|
||||
vaultId: "/subscriptions/sub1/resourceGroups/rg-vault/providers/Microsoft.RecoveryServices/vaults/vault1"
|
||||
host_await:
|
||||
- response:
|
||||
name: "some-other-link"
|
||||
properties:
|
||||
targetid: "/subscriptions/sub1/resourceGroups/rg-vault/providers/Microsoft.RecoveryServices/vaults/vault1/replicationProtectedItems/item1"
|
||||
want_effect: "DeployIfNotExists"
|
||||
|
||||
# =========================================================================
|
||||
# Inclusion tag type — VM has matching tag → if-condition matches
|
||||
# =========================================================================
|
||||
|
||||
- note: dine_inclusion_tag_match
|
||||
resource:
|
||||
type: "Microsoft.Compute/virtualMachines"
|
||||
name: "vm-tagged"
|
||||
location: "eastus"
|
||||
tags:
|
||||
Environment: "production"
|
||||
properties: {}
|
||||
parameters:
|
||||
sourceRegion: "eastus"
|
||||
targetRegion: "westus"
|
||||
targetResourceGroupId: "/subscriptions/sub1/resourceGroups/rg-dr"
|
||||
vaultResourceGroupId: "/subscriptions/sub1/resourceGroups/rg-vault"
|
||||
vaultId: "/subscriptions/sub1/resourceGroups/rg-vault/providers/Microsoft.RecoveryServices/vaults/vault1"
|
||||
tagName: "Environment"
|
||||
tagValue:
|
||||
- "production"
|
||||
- "staging"
|
||||
tagType: "Inclusion"
|
||||
host_await:
|
||||
- response: null
|
||||
want_effect: "DeployIfNotExists"
|
||||
|
||||
# =========================================================================
|
||||
# Inclusion tag type — VM tag doesn't match → if-condition fails → pass
|
||||
# =========================================================================
|
||||
|
||||
- note: pass_inclusion_tag_no_match
|
||||
resource:
|
||||
type: "Microsoft.Compute/virtualMachines"
|
||||
name: "vm-dev"
|
||||
location: "eastus"
|
||||
tags:
|
||||
Environment: "development"
|
||||
properties: {}
|
||||
parameters:
|
||||
sourceRegion: "eastus"
|
||||
targetRegion: "westus"
|
||||
targetResourceGroupId: "/subscriptions/sub1/resourceGroups/rg-dr"
|
||||
vaultResourceGroupId: "/subscriptions/sub1/resourceGroups/rg-vault"
|
||||
vaultId: "/subscriptions/sub1/resourceGroups/rg-vault/providers/Microsoft.RecoveryServices/vaults/vault1"
|
||||
tagName: "Environment"
|
||||
tagValue:
|
||||
- "production"
|
||||
- "staging"
|
||||
tagType: "Inclusion"
|
||||
want_undefined: true
|
||||
|
||||
# =========================================================================
|
||||
# Exclusion tag type — VM tag in exclusion list → pass (excluded from scope)
|
||||
# =========================================================================
|
||||
|
||||
- note: pass_exclusion_tag_excluded
|
||||
resource:
|
||||
type: "Microsoft.Compute/virtualMachines"
|
||||
name: "vm-excluded"
|
||||
location: "eastus"
|
||||
tags:
|
||||
Environment: "sandbox"
|
||||
properties: {}
|
||||
parameters:
|
||||
sourceRegion: "eastus"
|
||||
targetRegion: "westus"
|
||||
targetResourceGroupId: "/subscriptions/sub1/resourceGroups/rg-dr"
|
||||
vaultResourceGroupId: "/subscriptions/sub1/resourceGroups/rg-vault"
|
||||
vaultId: "/subscriptions/sub1/resourceGroups/rg-vault/providers/Microsoft.RecoveryServices/vaults/vault1"
|
||||
tagName: "Environment"
|
||||
tagValue:
|
||||
- "sandbox"
|
||||
- "test"
|
||||
tagType: "Exclusion"
|
||||
want_undefined: true
|
||||
|
||||
# =========================================================================
|
||||
# Exclusion tag type — VM tag NOT in exclusion list → matches → DINE
|
||||
# =========================================================================
|
||||
|
||||
- note: dine_exclusion_tag_not_excluded
|
||||
resource:
|
||||
type: "Microsoft.Compute/virtualMachines"
|
||||
name: "vm-prod-excl"
|
||||
location: "eastus"
|
||||
tags:
|
||||
Environment: "production"
|
||||
properties: {}
|
||||
parameters:
|
||||
sourceRegion: "eastus"
|
||||
targetRegion: "westus"
|
||||
targetResourceGroupId: "/subscriptions/sub1/resourceGroups/rg-dr"
|
||||
vaultResourceGroupId: "/subscriptions/sub1/resourceGroups/rg-vault"
|
||||
vaultId: "/subscriptions/sub1/resourceGroups/rg-vault/providers/Microsoft.RecoveryServices/vaults/vault1"
|
||||
tagName: "Environment"
|
||||
tagValue:
|
||||
- "sandbox"
|
||||
- "test"
|
||||
tagType: "Exclusion"
|
||||
host_await:
|
||||
- response: null
|
||||
want_effect: "DeployIfNotExists"
|
||||
|
||||
# =========================================================================
|
||||
# Wrong location → if-condition fails → pass
|
||||
# =========================================================================
|
||||
|
||||
- note: pass_wrong_location
|
||||
resource:
|
||||
type: "Microsoft.Compute/virtualMachines"
|
||||
name: "vm-wrong-loc"
|
||||
location: "westus"
|
||||
properties: {}
|
||||
parameters:
|
||||
sourceRegion: "eastus"
|
||||
targetRegion: "westus"
|
||||
targetResourceGroupId: "/subscriptions/sub1/resourceGroups/rg-dr"
|
||||
vaultResourceGroupId: "/subscriptions/sub1/resourceGroups/rg-vault"
|
||||
vaultId: "/subscriptions/sub1/resourceGroups/rg-vault/providers/Microsoft.RecoveryServices/vaults/vault1"
|
||||
want_undefined: true
|
||||
150
tests/azure_policy/cases/e2e_vm_skus_allowed.yaml
Normal file
150
tests/azure_policy/cases/e2e_vm_skus_allowed.yaml
Normal file
@@ -0,0 +1,150 @@
|
||||
# Copyright (c) Microsoft Corporation.
|
||||
# Licensed under the MIT License.
|
||||
|
||||
# E2E Test: Compute/VMSkusAllowed_Deny
|
||||
# Real Azure Policy: "Allowed virtual machine size SKUs"
|
||||
# Features: allOf, not, field (type + alias), equals, in, parameters()
|
||||
|
||||
aliases: test_aliases.json
|
||||
|
||||
policy_definition: |
|
||||
{
|
||||
"properties": {
|
||||
"displayName": "Allowed virtual machine size SKUs",
|
||||
"policyType": "BuiltIn",
|
||||
"mode": "Indexed",
|
||||
"parameters": {
|
||||
"listOfAllowedSKUs": {
|
||||
"type": "Array",
|
||||
"metadata": {
|
||||
"displayName": "Allowed Size SKUs"
|
||||
}
|
||||
}
|
||||
},
|
||||
"policyRule": {
|
||||
"if": {
|
||||
"allOf": [
|
||||
{
|
||||
"field": "type",
|
||||
"equals": "Microsoft.Compute/virtualMachines"
|
||||
},
|
||||
{
|
||||
"not": {
|
||||
"field": "Microsoft.Compute/virtualMachines/sku.name",
|
||||
"in": "[parameters('listOfAllowedSKUs')]"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"then": {
|
||||
"effect": "Deny"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cases:
|
||||
# =========================================================================
|
||||
# Deny — VM SKU not in allowed list
|
||||
# =========================================================================
|
||||
|
||||
- note: deny_disallowed_sku
|
||||
resource:
|
||||
type: "Microsoft.Compute/virtualMachines"
|
||||
name: "myVM"
|
||||
location: "eastus"
|
||||
properties:
|
||||
hardwareProfile:
|
||||
vmSize: "Standard_E64i_v3"
|
||||
parameters:
|
||||
listOfAllowedSKUs:
|
||||
- "Standard_D2s_v3"
|
||||
- "Standard_D4s_v3"
|
||||
- "Standard_D8s_v3"
|
||||
want_effect: "Deny"
|
||||
|
||||
# =========================================================================
|
||||
# No effect — VM SKU is in allowed list
|
||||
# =========================================================================
|
||||
|
||||
- note: allow_permitted_sku
|
||||
resource:
|
||||
type: "Microsoft.Compute/virtualMachines"
|
||||
name: "myVM"
|
||||
location: "eastus"
|
||||
properties:
|
||||
hardwareProfile:
|
||||
vmSize: "Standard_D4s_v3"
|
||||
parameters:
|
||||
listOfAllowedSKUs:
|
||||
- "Standard_D2s_v3"
|
||||
- "Standard_D4s_v3"
|
||||
- "Standard_D8s_v3"
|
||||
want_undefined: true
|
||||
|
||||
# =========================================================================
|
||||
# No effect — wrong resource type (not a VM)
|
||||
# =========================================================================
|
||||
|
||||
- note: skip_wrong_resource_type
|
||||
resource:
|
||||
type: "Microsoft.Storage/storageAccounts"
|
||||
name: "myStorage"
|
||||
location: "eastus"
|
||||
properties: {}
|
||||
parameters:
|
||||
listOfAllowedSKUs:
|
||||
- "Standard_D2s_v3"
|
||||
want_undefined: true
|
||||
|
||||
# =========================================================================
|
||||
# Deny — single allowed SKU, VM doesn't match
|
||||
# =========================================================================
|
||||
|
||||
- note: deny_single_allowed_sku
|
||||
resource:
|
||||
type: "Microsoft.Compute/virtualMachines"
|
||||
name: "bigVM"
|
||||
location: "westus"
|
||||
properties:
|
||||
hardwareProfile:
|
||||
vmSize: "Standard_M128s"
|
||||
parameters:
|
||||
listOfAllowedSKUs:
|
||||
- "Standard_B1s"
|
||||
want_effect: "Deny"
|
||||
|
||||
# =========================================================================
|
||||
# No effect — empty allowed list means everything blocked? No: `in` with
|
||||
# empty array is always false → not(false)=true → Deny.
|
||||
# =========================================================================
|
||||
|
||||
- note: deny_empty_allowed_list
|
||||
resource:
|
||||
type: "Microsoft.Compute/virtualMachines"
|
||||
name: "anyVM"
|
||||
location: "eastus"
|
||||
properties:
|
||||
hardwareProfile:
|
||||
vmSize: "Standard_D2s_v3"
|
||||
parameters:
|
||||
listOfAllowedSKUs: []
|
||||
want_effect: "Deny"
|
||||
|
||||
# =========================================================================
|
||||
# Case sensitivity — SKU names are compared case-insensitively by the
|
||||
# `in` operator (Azure Policy string comparison semantics)
|
||||
# =========================================================================
|
||||
|
||||
- note: allow_case_insensitive_sku
|
||||
resource:
|
||||
type: "Microsoft.Compute/virtualMachines"
|
||||
name: "myVM"
|
||||
location: "eastus"
|
||||
properties:
|
||||
hardwareProfile:
|
||||
vmSize: "standard_d4s_v3"
|
||||
parameters:
|
||||
listOfAllowedSKUs:
|
||||
- "Standard_D4s_v3"
|
||||
want_undefined: true
|
||||
157
tests/azure_policy/cases/e2e_vnet_ddos_modify.yaml
Normal file
157
tests/azure_policy/cases/e2e_vnet_ddos_modify.yaml
Normal file
@@ -0,0 +1,157 @@
|
||||
# Copyright (c) Microsoft Corporation.
|
||||
# Licensed under the MIT License.
|
||||
|
||||
# E2E Test: Network/VirtualNetworkDdosStandard_Audit
|
||||
# Real Azure Policy: "Virtual networks should be protected by Azure DDoS Protection"
|
||||
# Source: regolator/policyDefinitions/Network/VirtualNetworkDdosStandard_Audit.json
|
||||
#
|
||||
# Features exercised:
|
||||
# - Modify effect with operations (addOrReplace)
|
||||
# - anyOf within allOf (enableDdosProtection OR ddosProtectionPlan empty)
|
||||
# - Parameterized effect with Modify/Audit/Disabled
|
||||
# - conflictEffect in details
|
||||
# - field equals "" (empty string check)
|
||||
|
||||
aliases: test_aliases.json
|
||||
|
||||
policy_definition: |
|
||||
{
|
||||
"properties": {
|
||||
"displayName": "Virtual networks should be protected by Azure DDoS Protection",
|
||||
"policyType": "BuiltIn",
|
||||
"mode": "All",
|
||||
"parameters": {
|
||||
"effect": {
|
||||
"type": "String",
|
||||
"allowedValues": ["Modify", "Audit", "Disabled"],
|
||||
"defaultValue": "Modify"
|
||||
},
|
||||
"ddosPlan": {
|
||||
"type": "String",
|
||||
"metadata": {
|
||||
"displayName": "DDoS Protection Plan",
|
||||
"strongType": "Microsoft.Network/ddosProtectionPlans"
|
||||
}
|
||||
}
|
||||
},
|
||||
"policyRule": {
|
||||
"if": {
|
||||
"allOf": [
|
||||
{
|
||||
"field": "type",
|
||||
"equals": "Microsoft.Network/virtualNetworks"
|
||||
},
|
||||
{
|
||||
"anyOf": [
|
||||
{
|
||||
"field": "Microsoft.Network/virtualNetworks/enableDdosProtection",
|
||||
"notEquals": true
|
||||
},
|
||||
{
|
||||
"field": "Microsoft.Network/virtualNetworks/ddosProtectionPlan",
|
||||
"equals": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"then": {
|
||||
"effect": "[parameters('effect')]",
|
||||
"details": {
|
||||
"conflictEffect": "audit",
|
||||
"roleDefinitionIds": [
|
||||
"/providers/microsoft.authorization/roleDefinitions/4d97b98b-1d4f-4787-a291-c67834d212e7"
|
||||
],
|
||||
"operations": [
|
||||
{
|
||||
"operation": "addOrReplace",
|
||||
"field": "Microsoft.Network/virtualNetworks/enableDdosProtection",
|
||||
"value": true
|
||||
},
|
||||
{
|
||||
"operation": "addOrReplace",
|
||||
"field": "Microsoft.Network/virtualNetworks/ddosProtectionPlan.id",
|
||||
"value": "[parameters('ddosPlan')]"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cases:
|
||||
# =========================================================================
|
||||
# Non-compliant: DDoS not enabled
|
||||
# =========================================================================
|
||||
|
||||
- note: modify_ddos_not_enabled
|
||||
resource:
|
||||
type: "Microsoft.Network/virtualNetworks"
|
||||
name: "vnet-no-ddos"
|
||||
properties:
|
||||
enableDdosProtection: false
|
||||
ddosProtectionPlan: ""
|
||||
parameters:
|
||||
ddosPlan: "/subscriptions/sub1/resourceGroups/rg1/providers/Microsoft.Network/ddosProtectionPlans/plan1"
|
||||
want_effect: "Modify"
|
||||
want_details:
|
||||
roleDefinitionIds:
|
||||
- "/providers/microsoft.authorization/roleDefinitions/4d97b98b-1d4f-4787-a291-c67834d212e7"
|
||||
operations:
|
||||
- operation: "addOrReplace"
|
||||
field: "Microsoft.Network/virtualNetworks/enableDdosProtection"
|
||||
value: true
|
||||
- operation: "addOrReplace"
|
||||
field: "Microsoft.Network/virtualNetworks/ddosProtectionPlan.id"
|
||||
value: "/subscriptions/sub1/resourceGroups/rg1/providers/Microsoft.Network/ddosProtectionPlans/plan1"
|
||||
|
||||
- note: modify_ddos_enabled_but_no_plan
|
||||
resource:
|
||||
type: "Microsoft.Network/virtualNetworks"
|
||||
name: "vnet-no-plan"
|
||||
properties:
|
||||
enableDdosProtection: true
|
||||
ddosProtectionPlan: ""
|
||||
parameters:
|
||||
ddosPlan: "/subscriptions/sub1/resourceGroups/rg1/providers/Microsoft.Network/ddosProtectionPlans/plan1"
|
||||
want_effect: "Modify"
|
||||
|
||||
- note: modify_ddos_missing_field
|
||||
resource:
|
||||
type: "Microsoft.Network/virtualNetworks"
|
||||
name: "vnet-missing"
|
||||
properties: {}
|
||||
parameters:
|
||||
ddosPlan: "/subscriptions/sub1/resourceGroups/rg1/providers/Microsoft.Network/ddosProtectionPlans/plan1"
|
||||
want_effect: "Modify"
|
||||
|
||||
# =========================================================================
|
||||
# Compliant: DDoS enabled with plan
|
||||
# =========================================================================
|
||||
|
||||
- note: pass_ddos_enabled_with_plan
|
||||
resource:
|
||||
type: "Microsoft.Network/virtualNetworks"
|
||||
name: "vnet-protected"
|
||||
properties:
|
||||
enableDdosProtection: true
|
||||
ddosProtectionPlan:
|
||||
id: "/subscriptions/sub1/resourceGroups/rg1/providers/Microsoft.Network/ddosProtectionPlans/plan1"
|
||||
parameters:
|
||||
ddosPlan: "/subscriptions/sub1/resourceGroups/rg1/providers/Microsoft.Network/ddosProtectionPlans/plan1"
|
||||
want_undefined: true
|
||||
|
||||
# =========================================================================
|
||||
# Wrong type
|
||||
# =========================================================================
|
||||
|
||||
- note: pass_wrong_type
|
||||
resource:
|
||||
type: "Microsoft.Storage/storageAccounts"
|
||||
name: "not-a-vnet"
|
||||
properties:
|
||||
enableDdosProtection: false
|
||||
parameters:
|
||||
ddosPlan: "/subscriptions/sub1/resourceGroups/rg1/providers/Microsoft.Network/ddosProtectionPlans/plan1"
|
||||
want_undefined: true
|
||||
Reference in New Issue
Block a user