Azure Policy to Audit NSG rule sourceAddressPrefix - azure

In Azure I have an NSG Rule configured as follows:
Im trying to write an Azure Policy, to audit if the Source IP addresses/CIDR ranges is not set correctly.
The value should always be exactly equal to: 192.168.0.0/24,192.168.1.0/24.
If it is not that exact value, it should audit.
This is the definition I have written:
{
"if": {
"allOf": [
{
"field": "type",
"equals": "Microsoft.Network/networkSecurityGroups"
},
{
"field": "name",
"like": "jeffweb2-dr-sm-nsg"
},
{
"count": {
"field": "Microsoft.Network/networkSecurityGroups/securityRules[*]",
"where": {
"allOf": [
{
"field": "Microsoft.Network/networkSecurityGroups/securityRules[*].name",
"equals": "SQL"
},
{
"anyof": [
{
"field": "Microsoft.Network/networkSecurityGroups/securityRules[*].sourceAddressPrefix",
"notEquals": [
"192.168.0.0/24",
"192.168.1.0/24"
]
}
]
}
]
}
},
"greater": 0
}
]
},
"then": {
"effect": "audit"
}
}
But when trying to create the definition with this json, I get the error:
New-AzPolicyDefinition : InvalidPolicyRule : Failed to parse policy rule: 'Error reading string. Unexpected token: StartArray. Path 'notEquals'.'.
Question: How do you pass multiple CIDR ranges into the notEquals, I believe this is my problem I believe.

Instead of notEquals, you can use notIn condition to check the values in array. notIn works as it expects an array and you can get rid of above error.
{
"if": {
"allOf": [
{
"field": "type",
"equals": "Microsoft.Network/networkSecurityGroups"
},
{
"field": "name",
"like": "jeffweb2-dr-sm-nsg"
},
{
"count": {
"field": "Microsoft.Network/networkSecurityGroups/securityRules[*]",
"where": {
"allOf": [
{
"field": "Microsoft.Network/networkSecurityGroups/securityRules[*].name",
"equals": "SQL"
},
{
"anyof": [
{
"field": "Microsoft.Network/networkSecurityGroups/securityRules[*].sourceAddressPrefix",
"notIn": [
"192.168.0.0/24",
"192.168.1.0/24"
]
}
]
}
]
}
},
"greater": 0
}
]
},
"then": {
"effect": "audit"
}
}

Related

Azure Policy to deny traffic if source address prefixes are not within the private range

I am trying to modify an existing custom policy which I obtained from github. The goal is to not allow users to RDP from public IP addresses. The ARM template needs to support only private ranges. Here is the code:
{
"mode": "All",
"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"
},
{
"value": "[or(ipRangeContains('10.0.0.0/8', field('Microsoft.Network/networkSecurityGroups/securityRules/sourceAddressPrefix')),ipRangeContains('172.16.0.0/12', field('Microsoft.Network/networkSecurityGroups/securityRules/sourceAddressPrefix')),ipRangeContains('192.168.0.0/16', field('Microsoft.Network/networkSecurityGroups/securityRules/sourceAddressPrefix')))]",
"equals": false
},
{
"count": {
"field": "Microsoft.Network/networkSecurityGroups/securityRules/sourceAddressPrefixes[*]",
"where": {
"value": "[or(ipRangeContains('10.0.0.0/8', first(field('Microsoft.Network/networkSecurityGroups/securityRules/sourceAddressPrefixes[*]'))),ipRangeContains('172.16.0.0/12', first(field('Microsoft.Network/networkSecurityGroups/securityRules/sourceAddressPrefixes[*]'))),ipRangeContains('192.168.0.0/16', first(field('Microsoft.Network/networkSecurityGroups/securityRules/sourceAddressPrefixes[*]'))))]",
"equals": false
}
},
"greater": 0
},
{
"not": {
"field": "Microsoft.Network/networkSecurityGroups/securityRules/sourceAddressPrefixes[*]",
"notEquals": "*"
}
},
{
"not": {
"field": "Microsoft.Network/networkSecurityGroups/securityRules/sourceAddressPrefixes[*]",
"notEquals": "Internet"
}
}
]
}
]
}
]
},
"then": {
"effect": "deny"
}
},
"parameters": {}
}
The following section works well. It does not allow me to add IP addresses other than private IP ranges. So policy denies creation of NSG rule with MY PUBLIC IP address.
{
"value": "[or(ipRangeContains('10.0.0.0/8', field('Microsoft.Network/networkSecurityGroups/securityRules/sourceAddressPrefix')),ipRangeContains('172.16.0.0/12', field('Microsoft.Network/networkSecurityGroups/securityRules/sourceAddressPrefix')),ipRangeContains('192.168.0.0/16', field('Microsoft.Network/networkSecurityGroups/securityRules/sourceAddressPrefix')))]",
"equals": false
}
The next section throws an error
{
"count": {
"field": "Microsoft.Network/networkSecurityGroups/securityRules/sourceAddressPrefixes[*]",
"where": {
"value": "[or(ipRangeContains('10.0.0.0/8', first(field('Microsoft.Network/networkSecurityGroups/securityRules/sourceAddressPrefixes[*]'))),ipRangeContains('172.16.0.0/12', first(field('Microsoft.Network/networkSecurityGroups/securityRules/sourceAddressPrefixes[*]'))),ipRangeContains('192.168.0.0/16', first(field('Microsoft.Network/networkSecurityGroups/securityRules/sourceAddressPrefixes[*]'))))]",
"equals": false
}
},
"greater": 0
}
So, if I enter multiple Private IP's separated by comma, when creating NSG for SourceAddress, I get the following error:
The inner error is 'The policy language function 'ipRangeContains' has encountered one or more invalid IP Ranges: '"10.0.0.0/8",null'. IP Ranges can be specified in CIDR notation, single IP address or a range with start and end addresses separated with a '-'. Ranges that mix between IPv4 and IPv6 and ranges that don't include any addresses are not allowed.'.
Any ideas/suggestions on fixing this error will be great help..
I had to include logic for not empty source IP fields and check them to not match * and Internet.
{
"mode": "All",
"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"
},
{
"value": "[if(and(not(empty(field('Microsoft.Network/networkSecurityGroups/securityRules/sourceAddressPrefix'))),not(contains(field('Microsoft.Network/networkSecurityGroups/securityRules/sourceAddressPrefix'),'*')),not(contains(field('Microsoft.Network/networkSecurityGroups/securityRules/sourceAddressPrefix'),'Internet'))),not(or(ipRangeContains('10.0.0.0/8', field('Microsoft.Network/networkSecurityGroups/securityRules/sourceAddressPrefix')),ipRangeContains('172.16.0.0/12', field('Microsoft.Network/networkSecurityGroups/securityRules/sourceAddressPrefix')),ipRangeContains('192.168.0.0/16', field('Microsoft.Network/networkSecurityGroups/securityRules/sourceAddressPrefix')))),'false')]",
"equals": true
},
{
"count": {
"field": "Microsoft.Network/networkSecurityGroups/securityRules/sourceAddressPrefixes[*]",
"where": {
"value": "[or(ipRangeContains('10.0.0.0/8', first(field('Microsoft.Network/networkSecurityGroups/securityRules/sourceAddressPrefixes[*]'))),ipRangeContains('172.16.0.0/12', first(field('Microsoft.Network/networkSecurityGroups/securityRules/sourceAddressPrefixes[*]'))),ipRangeContains('192.168.0.0/16', first(field('Microsoft.Network/networkSecurityGroups/securityRules/sourceAddressPrefixes[*]'))))]",
"equals": false
}
},
"greater": 0
},
{
"not": {
"field": "Microsoft.Network/networkSecurityGroups/securityRules/sourceAddressPrefixes[*]",
"notEquals": "*"
}
},
{
"not": {
"field": "Microsoft.Network/networkSecurityGroups/securityRules/sourceAddressPrefixes[*]",
"notEquals": "Internet"
}
}
]
}
]
}
]
},
"then": {
"effect": "deny"
}
},
"parameters": {}
}

Azure Policy: Assert if an array contains an array

Im trying to create an Azure Policy definition that audits whether a NSG contains a certain rule.
So in the following code im trying to audit NSGs that dont have this rule:
name: deny-rule, access: deny, sourceAddresses: 10.124.0.0/16,... (see code)
It doesnt seem to work, Im struggling with the sourceAddresses array part, which I want to ensure that rule contains all 4 items in the array exactly.
"policyRule": {
"if": {
"allOf": [
{
"field": "type",
"equals": "Microsoft.Network/networkSecurityGroups"
},
{
"count": {
"field": "Microsoft.Network/networkSecurityGroups/securityRules[*]",
"where": {
"allOf": [
{
"field": "Microsoft.Network/networkSecurityGroups/securityRules[*].name",
"equals": "deny-rule"
},
{
"field": "Microsoft.Network/networkSecurityGroups/securityRules[*].access",
"equals": "deny"
},
{
"count": {
"value": [
"10.124.0.0/16",
"10.125.0.0/16",
"10.74.0.0/16",
"10.75.0.0/16"
],
"name": "ranges",
"where": {
"field": "Microsoft.Network/networkSecurityGroups/securityRules[*].sourceAddressPrefixes[*]",
"equals": "[current('ranges')]"
}
},
"equals": 4
}
]
}
},
"less": 1
}
]
},
"then": {
"effect": "audit"
}
}
**Update: I think this is the right direction, https://learn.microsoft.com/en-us/azure/governance/policy/concepts/definition-structure#value-count-examples

How do I combine multiple statements in an Azure policy definition?

I want to create a custom Azure Policy JSON that reads through Azure resources and makes sure that it is following our standardized naming convention. For example, I am trying to set it up for virtual machines, cloud services, and Redis cache.
{
"if": {
"allof": [
{
"field": "type",
"equals": "Microsoft.Compute/virtualMachines"
},
{
"not": {
"anyOf": [
{
"field": "name",
"match": "gz?????????#?##"
}
]
}
}
]
},
"then": {
"effect": "[parameters('effect')]"
}
"if": {
"allof": [
{
"field": "type",
"equals": "Microsoft.ClassicCompute/domainNames"
},
{
"not": {
"anyOf": [
{
"field": "name",
"match": "GZ?-??????-??#-???-??????-###"
}
]
}
}
]
},
"then": {
"effect": "[parameters('effect')]"
}
"if": {
"allof": [
{
"field": "type",
"equals": "Microsoft.Cache/Redis"
},
{
"not": {
"anyOf": [
{
"field": "name",
"match": "gz?????????#???###"
}
]
}
}
]
},
"then": {
"effect": "[parameters('effect')]"
}
}
I don't think Azure allows for there to be multiple IFs like how I am trying to set it up. What I want it to do is this:
If the resource is a VM and it does not match that convention, then audit.
If the resource is a cloud service and it does not match that convention, then audit.
If the resource is Redis cache and it does not match that convention, then audit.
Updated JSON
You should use an initiative (policy set definition) to organize multiple related policies like this. This will be easier to maintain than a single policy definition with conditions for each resource type, and will allow you to see compliance results both for the entire naming convention policy set, and for each individual policy.
For example:
"properties": {
"displayName": "Naming conventions",
"policyType": "Custom",
"parameters": {
"effect": {
"type": "String",
"defaultValue": "Audit"
}
},
"policyDefinitions": [
{
"policyDefinitionId": "/subscriptions/<SUBSCRIPTION ID>/providers/Microsoft.Authorization/policyDefinitions/<YOUR VIRTUAL MACHINE NAMING CONVENTION POLICY ID>",
"parameters": {
"effect": {
"value": "[parameters('effect')]"
}
}
},
{
"policyDefinitionId": "/subscriptions/<SUBSCRIPTION ID>/providers/Microsoft.Authorization/policyDefinitions/<YOUR DOMAIN NAME NAMING CONVENTION POLICY ID>",
"parameters": {
"effect": {
"value": "[parameters('effect')]"
}
}
}
]
}
}
I wouldn't recommend it, but if you must combine multiple types in a single definition, then you may use anyOf, for example:
{
"if": {
"anyOf": [
{
"allOf": [
{
"field": "type",
"equals": "Microsoft.Compute/virtualMachines"
},
{
"not": {
"field": "name",
"match": "gz?????????#?##"
}
}
]
},
{
"allOf": [
{
"field": "type",
"equals": "Microsoft.ClassicCompute/domainNames"
},
{
"not": {
"field": "name",
"match": "GZ?-??????-??#-???-??????-###"
}
}
]
}
]
},
"then" : {
"effect" : "audit"
}
}

Azure policy reporting extra resources as non-compliant

I copied sample from: https://github.com/Azure/azure-policy/blob/master/samples/Network/no-route-table-in-ER-Network/azurepolicy.rules.json and instead tried to create policy which would deny subnets without NSG.
{
"if": {
"anyOf": [
{
"allOf": [
{
"field": "type",
"equals": "Microsoft.Network/virtualNetworks"
},
{
"field": "Microsoft.Network/virtualNetworks/subnets[*].networkSecurityGroup.id",
"exists": false
}
]
},
{
"allOf": [
{
"field": "type",
"equals": "Microsoft.Network/virtualNetworks/subnets"
},
{
"field": "Microsoft.Network/virtualNetworks/subnets/networkSecurityGroup.id",
"exists": false
}
]
}
]
},
"then": {
"effect": "deny"
}
}
Policy works fine and stops creating subnets without assigning NSG and removing NSG from subnet. However, it also reports the virtual network as non-compliant even though virtual network would be fine. How can I make this policy to only report subnets and not the virtual network?
I managed to get this working by little bit changing the logic:
{
"if": {
"anyOf": [
{
"allOf": [
{
"field": "type",
"equals": "Microsoft.Network/virtualNetworks"
},
{
"not": {
"field": "Microsoft.Network/virtualNetworks/subnets[*].networkSecurityGroup.id",
"exists": true
}
}
]
},
{
"allOf": [
{
"field": "type",
"equals": "Microsoft.Network/virtualNetworks/subnets"
},
{
"not": {
"field": "Microsoft.Network/virtualNetworks/subnets/networkSecurityGroup.id",
"exists": true
}
}
]
}
]
},
"then": {
"effect": "deny"
}
}

Azure Policy Deny :if one of the tag not present in the resource group name

I've created an Azure Policy, i wanted to deny the resource group creation if user doesn't specify tag with key "Env" or "use"
But when i create the resource group with Env tag it blocks me, it only allows me when i add both the tag which is env and use.
As per my understanding "anyof" in azure policy is used as "OR" but my code isn't behaving the same wa
{
"if": {
"allOf": [
{
"field": "type",
"equals": "Microsoft.Resources/subscriptions/resourceGroups"
},
{
"anyof": [
{
"field": "tags.Env",
"exists": false
},
{
"field": "tags.use",
"exists": false
}
]
}
]
},
"then": {
"effect": "deny"
}
}
Based on the Chris's suggestion I've worked on the tag name and values but it is giving me an error in the policy and it is not taking the "NOT"
{
"mode": "all",
"policyRule": {
"if": {
"allOf": [
{
"field": "type",
"equals": "Microsoft.Resources/subscriptions/resourceGroups"
},
{
"not":{
{
"field": "tags.Env",
"equals" : "Prod"
},
{
"field": "tags.OS",
"equals" : "windows"
}
}
}
]
},
"then": {
"effect": "deny"
}
},
"parameters": {}
}
Right now, like you mentioned, the policy is evaluating if "tags.Env doesn't exist OR tags.use doesn't exist". If either tag does not exist you will be denied.
What you want is to deny if "tags.Env doesn't exist AND tags.use doesn't exist". That would imply that they are both missing which is what you are trying to prevent.
{
"if": {
"allOf": [
{
"field": "type",
"equals": "Microsoft.Resources/subscriptions/resourceGroups"
},
{
"field": "tags.Env",
"exists": false
},
{
"field": "tags.use",
"exists": false
}
]
},
"then": {
"effect": "deny"
}
}

Resources