Azure Policy Not Denying Route Creation - json

I want to make an Azure policy that denies anyone trying to create a route to certain prefixes that don't use the next hop virtual appliance parameter and IP that I specify. I have this working with just a single prefix (0.0.0.0/0 internet route) but as soon as I try to define other routes (10.0.0.0/8) it doesn't work. Here is what I have so far:
{
"mode": "All",
"policyRule": {
"if": {
"anyOf": [
{
"allOf": [
{
"field": "type",
"equals": "Microsoft.Network/routeTables"
},
{
"count": {
"field": "Microsoft.Network/routeTables/routes[*]",
"where": {
"anyOf": [
{
"field": "Microsoft.Network/routeTables/routes[*].addressPrefix",
"equals": "0.0.0.0/0"
},
{
"anyOf": [
{
"field": "Microsoft.Network/routeTables/routes[*].nextHopType",
"notEquals": "VirtualAppliance"
},
{
"field": "Microsoft.Network/routeTables/routes[*].nextHopIpAddress",
"notEquals": "[parameters('routeTableSettings')[field('location')].virtualApplianceIpAddress]"
}
]
},
{
"field": "Microsoft.Network/routeTables/routes[*].addressPrefix",
"equals": "10.0.0.0/8"
},
{
"anyOf": [
{
"field": "Microsoft.Network/routeTables/routes[*].nextHopType",
"notEquals": "VirtualAppliance"
},
{
"field": "Microsoft.Network/routeTables/routes[*].nextHopIpAddress",
"notEquals": "[parameters('routeTableSettings')[field('location')].virtualApplianceIpAddress]"
}
]
}
]
}
},
"greater": 0
}
]
},
{
"anyOf": [
{
"field": "type",
"equals": "Microsoft.Network/routeTables/routes"
},
{
"field": "Microsoft.Network/routeTables/routes[*].addressPrefix",
"equals": "0.0.0.0/0"
},
{
"anyOf": [
{
"field": "Microsoft.Network/routeTables/routes/nextHopType",
"notEquals": "VirtualAppliance"
},
{
"field": "Microsoft.Network/routeTables/routes/nextHopIpAddress",
"notEquals": "[parameters('routeTableSettings')[field('location')].virtualApplianceIpAddress]"
}
]
},
{
"field": "type",
"equals": "Microsoft.Network/routeTables/routes"
},
{
"field": "Microsoft.Network/routeTables/routes[*].addressPrefix",
"equals": "10.0.0.0/8"
},
{
"anyOf": [
{
"field": "Microsoft.Network/routeTables/routes/nextHopType",
"notEquals": "VirtualAppliance"
},
{
"field": "Microsoft.Network/routeTables/routes/nextHopIpAddress",
"notEquals": "[parameters('routeTableSettings')[field('location')].virtualApplianceIpAddress]"
}
]
}
]
}
]
},
"then": {
"effect": "deny"
}
},
"parameters": {
"routeTableSettings": {
"type": "Object",
"metadata": {
"displayName": "Route Table Settings",
"description": "Location-specific settings for route tables."
}
}
}
}
Parameters
{
"eastus2": {
"virtualApplianceIpAddress": "10.1.1.1"
},
"disabled": {
"virtualApplianceIpAddress": ""
}
}

To achieve the above requirement we need to use IN clause in our azure policy to deny/not allow the resource type to create .
As suggested by #harshavmb in comment which is correct. Posting it as an answer to help other community members to find fix their issue for the same.
The MS DOC has an example how to use the in with policy rule:
{
"properties": {
"displayName": "Allowed locations",
"description": "This policy enables you to restrict the locations your organization can specify when deploying resources.",
"mode": "Indexed",
"metadata": {
"version": "1.0.0",
"category": "Locations"
},
"parameters": {
"allowedLocations": {
"type": "array",
"metadata": {
"description": "The list of locations that can be specified when deploying resources",
"strongType": "location",
"displayName": "Allowed locations"
},
"defaultValue": [ "westus2" ]
}
},
"policyRule": {
"if": {
"not": {
"field": "location",
"in": "[parameters('allowedLocations')]"
}
},
"then": {
"effect": "deny"
}
}
}
}
Also from Azure portal>Policy> Not allowed resource we can find the json with in clause .
As shown below:
For more information please refer the below links:-
MICROSOFT DOCUMENTATION:- Not_allowed_resource_types
BLOG:- AZURE POLICY TO DENY CREATION OF ALL RESOURCES

Related

Audit for specific Azure DNS in a specific region (Azure Policy)

I'd like to have an Azure Policy that audits for specific Azure DNS's in the targeted Region. Both should be available in an array so the policy can be scoped multiple times.
So far I've got this, which does not work since it puts the state in compliance by having the right DNS set, but completely ignores the region specified in the array. My goal is to have the policy compliance check for both.
{
"mode": "All",
"policyRule": {
"if": {
"allOf": [
{
"field": "type",
"equals": "Microsoft.Network/virtualNetworks"
},
{
"anyOf": [
{
"value": "[if(empty(field('Microsoft.Network/virtualNetworks/dhcpOptions.dnsServers')), bool('false'), equals(length(intersection(parameters('dnsSettings'), field('Microsoft.Network/virtualNetworks/dhcpOptions.dnsServers'))), length(parameters('dnsSettings'))))]",
"equals": false
},
{
"value": "[if(empty(field('Microsoft.Network/virtualNetworks/dhcpOptions.dnsServers')), bool('false'), equals(length(field('Microsoft.Network/virtualNetworks/dhcpOptions.dnsServers')),length(parameters('dnsSettings'))))]",
"equals": false
}
]
},
{
"not": {
"allOf": [
{
"field": "location",
"in": "[parameters('location')]"
}
]
}
}
]
},
"then": {
"effect": "[parameters('effect')]"
}
},
"parameters": {
"dnsSettings": {
"type": "Array",
"metadata": {
"displayName": "dnsSettings",
"description": "Audit for specific DNS settings."
}
},
"location": {
"type": "Array",
"metadata": {
"displayName": "Location",
"description": "Choose specific location",
"strongType": "location"
}
},
"effect": {
"type": "String",
"metadata": {
"displayName": "Effects",
"description": "Enable or disable the execution of the Policy."
},
"allowedValues": [
"Audit",
"Disabled"
],
"defaultValue": "Audit"
}
}
}
We can use auditIfNotExists with few logic operations so that this will allow only after the success code, below sample is to understand about .
{
"if": {
"field": "type",
"equals": "Microsoft.Compute/virtualMachines"
},
"then": {
"effect": "auditIfNotExists",
"details": {
"type": "Microsoft.Compute/virtualMachines/extensions",
"existenceCondition": {
"allOf": [{
"field": "Microsoft.Compute/virtualMachines/extensions/publisher",
"equals": "Microsoft.Azure.Security"
},
{
"field": "Microsoft.Compute/virtualMachines/extensions/type",
"equals": "IaaSAntimalware"
}
]
}
}
}
}
Below is the way we can specify the dns, Sample for adding dns:
{
"mode": "All",
"name": "Deny changing VNet DNS settings from pre-defined value",
"description": "This Policy will prevent users from changing DNS settings on a VNet",
"policyRule": {
"if": {
"allOf": [
{
"field": "type",
"equals": "Microsoft.Network/virtualNetworks"
},
{
"anyOf": [
{
"value": "[if(empty(field('Microsoft.Network/virtualNetworks/dhcpOptions.dnsServers')), bool('false'), equals(length(intersection(parameters('dnsSettings'), field('Microsoft.Network/virtualNetworks/dhcpOptions.dnsServers'))), length(parameters('dnsSettings'))))]",
"equals": false
},
{
"value": "[if(empty(field('Microsoft.Network/virtualNetworks/dhcpOptions.dnsServers')), bool('false'), equals(length(field('Microsoft.Network/virtualNetworks/dhcpOptions.dnsServers')),length(parameters('dnsSettings'))))]",
"equals": false
}
]
}
]
},
"then": {
"effect": "auditIfNotExists"
}
},
"parameters": {
"dnsSettings": {
"type": "array",
"metadata": {
"displayname": "Enforced DNS Settings",
"description": "Users will be unable to change the DNS settings on a VNet from the values defined in this array."
}
}
}
}
Check AzurePolicy.json and AzurePolicy.rules.json to understand about allowing standard dns within application rules. Include location settings in it.

Azure Policy for Naming

I'm doing something incorrectly here regarding an Azure Policy I'm creating. Trying to create a naming policy that blocks creation of a resource (in this case a resource group) that doesn't match.
{
"properties": {
"mode": "All",
"displayName": "Company Naming Convention - Resource Groups",
"description": "This policy governs the naming standard for resource groups and should be assigned at the resource group scope. The naming scheme is rg-region-workload name-environment-optional instance number'.",
"metadata": {
"category": "Governance"
},
"policyRule": {
"if": {
"allOf": [
{
"field": "type",
"equals": "Microsoft.Resources/resourceGroups"
},
{
"allOf": [
{
"field": "name",
"notLike": "rg-useast-*"
},
{
"field": "name",
"notLike": "rg-useast2-*"
},
{
"field": "name",
"notLike": "rg-uscentral-*"
},
{
"field": "name",
"notLike": "rg-uksouth-*"
}
]
},
{
"allOf": [
{
"field": "name",
"notLike": "*-production.###"
},
{
"field": "name",
"notLike": "*-development.###"
},
{
"field": "name",
"notLike": "*-qualityassurance.###"
},
{
"field": "name",
"notLike": "*-testing.###"
}
]
}
]
},
"then": {
"effect": "deny"
}
}
}
}
I'd ALSO like to create a policy to audit existing resources that don't match this name, but I can address that later. Anyone have a suggestion what I'm doing wrong or a better way to go about this?
Ok... so the original policy works great... if I actually looked at the right resource. Should be "Microsoft.Resources/subscriptions/resourceGroups" not "Microsoft.Resources/resourceGroups". Dang do I feel like an idiot...
You are using incorrect syntax in your Azure Policy Definition. The allOf syntax requires all conditions to be true and you can keep all the conditions in single allOf Operator.
Modified version of Policy for reference :
{
"properties": {
"mode": "All",
"displayName": "Company Naming Convention - Resource Groups",
"description": "This policy governs the naming standard for resource groups and should be assigned at the resource group scope. The naming scheme is rg-region-workload name-environment-optional instance number'.",
"metadata": {
"category": "Governance"
},
"policyRule": {
"if": {
"allOf": [
{
"field": "type",
"equals": "Microsoft.Resources/subscriptions/resourceGroups"
},
{
"field": "name",
"notLike": "rg-useast-*"
},
{
"field": "name",
"notLike": "rg-useast2-*"
},
{
"field": "name",
"notLike": "rg-uscentral-*"
},
{
"field": "name",
"notLike": "rg-uksouth-*"
},
{
"field": "name",
"notLike": "*-production.###"
},
{
"field": "name",
"notLike": "*-development.###"
},
{
"field": "name",
"notLike": "*-qualityassurance.###"
},
{
"field": "name",
"notLike": "*-testing.###"
}
]
},
"then": {
"effect": "deny"
}
}
}
}
Also note, it takes around 30 minutes for the assignment to be applied to the defined scope. For more information on evaluation cycle of Azure Policy, refer this document.
For on-demand evaluation , use az cli command : az policy state trigger-scan

How to enforce Tag value pattern in Azure policy?

I'd like to enforce tag value pattern "RJGVM-###" for a Tag which will be required for resource groups.
I manage to make it required, but whenever I put in any value it still passes.
{
"mode": "All",
"policyRule": {
"if": {
"allOf": [
{
"field": "type",
"equals": "Microsoft.Resources/subscriptions/resourceGroups"
},
{
"not": {
"field": "[concat('tags[',parameters('tagName'), ']')]",
"exists": "true"
}
},
{
"value": "[resourceGroup().tags[parameters('tagName')]]",
"notMatch": "RJGVM-###"
}
]
},
"then": {
"effect": "deny"
}
},
"parameters": {
"tagName": {
"type": "String",
"metadata": {
"displayName": "Tag Name",
"description": "Name of the tag, such as 'environment'"
}
}
}
}
Please refer this example mentioned in the below link to ensure match pattern on tag value :
https://github.com/Azure/azure-policy/tree/master/samples/TextPatterns/enforce-tag-match-pattern

JSON-Schema add property to the item conditionally

I have the following JSON data
[
{
"type": "social_media_profiles",
"data": {
"profiles": [
{
"key": "twitter",
"data": "username",
"field": "handle",
"label": "Tweet"
},
{
"key": "customLink",
"data": "abc",
"field": "url",
"label": "Click",
"color": {
"button": "red",
"text": "green",
"border": "yellow"
}
}
]
}
}
]
and following jsong schema
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "array",
"additionalProperties": false,
"items": {
"type": "object",
"required": ["type", "data"],
"additionalProperties": false,
"properties": {
"type": {
"type": "string",
"enum": [
"social_media_profiles"
]
},
"data": {
"type": "object"
}
},
"allOf": [
{
"if": {
"properties": {
"type": {
"const": "social_media_profiles"
}
}
},
"then": {
"properties": {
"data": {
"type": "object",
"required": ["profiles"],
"additionalProperties": false,
"properties": {
"profiles": {
"type": "array",
"items": {
"type": "object",
"required": ["data", "field", "key"],
"additionalProperties": false,
"properties": {
"data": {
"type": "string",
"description": "Data contains either profile url, handle id, etc."
},
"field": {
"type": "string",
"enum": ["url", "handle", "id", "tel"],
"description": "Type of field value."
},
"key": {
"type": "string",
"description": "Social media name used to distinguish each social network"
},
"label": {
"type": "string",
"description": "Label to display on the landing page"
}
},
"allOf": [
{
"if": {
"properties": {
"key": {
"const": "customLink"
}
}
},
"then": {
"properties": {
"color": {
"type": "object",
"additionalProperties": false,
"required": [],
"properties": {
"button": {
"type": "string"
},
"text": {
"type": "string"
},
"border": {
"type": "string"
}
}
}
}
}
}
]
}
}
}
}
}
}
}
]
}
}
I want to add new property color to the profiles item based on the condition when key of the item is customLink.
If key is not customLink then color property should not be there.
Validating the schema from https://www.jsonschemavalidator.net/ is giving error
Found 2 error(s)
Message: JSON does not match all schemas from 'allOf'. Invalid schema indexes: 0.
Schema path: #/items/allOf
Message: JSON does not match schema from 'then'.
Schema path: #/items/allOf/0/then/then
How can I append new property conditionally based on the sibling property value?
In your profiles.items schema, you defined additionalProperties: false.
additionalProperties depends on the properties defined in the same schema object, meaning that color would always be dissallowed.
You can separate out your concerns into "what properties are allowed", and "if their values are valid".
Moving the validation for the color object into profiles.properties allows you to maintain additionalProperties: false in that object level.
The schema values of the properties object are only applied to the instance location for the keys, if they exist (hence needing to use required to require that specific keys are... required).
Having simplified the condional section, you end up with a cleaner Schema.
You now only need to define the condition under which color is required, without worrying about what the value should look like.
Sudo code: if key is customLink, then require color, else dissallow color.
{
"if": {
"properties": {
"key": {
"const": "customLink"
}
}
},
"then": {
"required": [
"color"
]
},
"else": {
"not": {
"required": [
"color"
]
}
}
}
Using not, you can invert the validation result of the applied subschema, which is what you need to do when you want to define that a specific property is NOT allowed.
Here's a live demo to play with: https://jsonschema.dev/s/C9V6N
And for prosperity, here's the full schema, with one or two redundancies removed.
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "array",
"items": {
"type": "object",
"required": [
"type",
"data"
],
"additionalProperties": false,
"properties": {
"type": {
"type": "string",
"enum": [
"social_media_profiles"
]
},
"data": {
"type": "object"
}
},
"allOf": [
{
"properties": {
"data": {
"type": "object",
"required": [
"profiles"
],
"additionalProperties": false,
"properties": {
"profiles": {
"type": "array",
"items": {
"type": "object",
"required": [
"data",
"field",
"key"
],
"additionalProperties": false,
"properties": {
"data": {
"type": "string",
"description": "Data contains either profile url, handle id, etc."
},
"field": {
"type": "string",
"enum": [
"url",
"handle",
"id",
"tel"
],
"description": "Type of field value."
},
"key": {
"type": "string",
"description": "Social media name used to distinguish each social network"
},
"label": {
"type": "string",
"description": "Label to display on the landing page"
},
"color": {
"type": "object",
"additionalProperties": false,
"properties": {
"button": {
"type": "string"
},
"text": {
"type": "string"
},
"border": {
"type": "string"
}
}
}
},
"allOf": [
{
"if": {
"properties": {
"key": {
"const": "customLink"
}
}
},
"then": {
"required": [
"color"
]
},
"else": {
"not": {
"required": [
"color"
]
}
}
}
]
}
}
}
}
}
}
]
}
}

How to fix deployIfNotExists policy for Key Vault

Trying to create a DeployIfNotExists policy that will automatically set the "networkACLs" properties on all key vaults but after battling with that for a couple of weeks, I decided to try to manipulate a simpler boolean property instead of a complex object property. The property I chose is "enabledForDeployment". The policy does properly find the non-compliant key vaults but the deployment is not working.
Once I get this "easy" policy working, I will go back and attempt to set the "networkACLs" property to the following:
"networkAcls": {
"defaultAction": "Deny",
"bypass": "None",
"ipRules": [
{"value": "1.1.1.0/24"},
{"value":"2.2.2.0/24"}
],
"virtualNetworkRules": []
}
The policy code is below...
{
"mode": "All",
"policyRule": {
"if": {
"allof": [
{
"field": "type",
"equals": "Microsoft.KeyVault/vaults"
},
{
"not": {
"field": "Microsoft.KeyVault/vaults/enabledForDeployment",
"equals": true
}
}
]
},
"then": {
"effect": "deployIfNotExists",
"details": {
"type": "Microsoft.KeyVault/vaults",
"name": "[field('name')]",
"existenceCondition": {
"field": "Microsoft.KeyVault/vaults/enabledForDeployment",
"equals": "true"
},
"roleDefinitionIds": [
"/providers/Microsoft.Authorization/roleDefinitions/b24988ac-6180-42a0-ab88-20f7382dd24c"
],
"deployment": {
"location": "[field('location')]",
"properties": {
"mode": "incremental",
"template": {
"$schema": "http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"Name": {
"type": "string"
},
"location": {
"type": "string"
}
},
"resources": [
{
"type": "Microsoft.KeyVault/vaults",
"apiVersion": "2018-02-14",
"name": "[parameters('Name')]",
"location": "[parameters('location')]",
"properties": {
"enabledForDeployment": true
}
}
],
"outputs": {
"policy": {
"type": "string",
"value": "done"
}
}
},
"parameters": {
"location": {
"value": "[field('location')]"
},
"Name": {
"value": "[field('name')]"
}
}
}
}
}
}
},
"parameters": {}
}
I'm currently getting an "internalServerError" message. Any ideas?
#Kemley you are correct. My ARM template was incorrect. It was missing a few required fields (Sku, Access Policies, etc). Below is the final policy that updates the NetworkACLs if the default network allow all is set.
{
"properties": {
"displayName": "Vzn Deploy Key Vault NetworkAcls defaultAction",
"policyType": "Custom",
"mode": "All",
"description": "Removes the default allow all networks. Manually sets 2 firewall rules",
"parameters": {
"effect": {
"type": "String",
"metadata": {
"displayName": "Effect",
"description": "Enable or disable the execution of the policy"
},
"allowedValues": [
"deployIfNotExists",
"disabled"
],
"defaultValue": "deployIfNotExists"
}
},
"policyRule": {
"if": {
"field": "type",
"equals": "Microsoft.KeyVault/vaults"
},
"then": {
"effect": "[parameters('effect')]",
"details": {
"type": "Microsoft.KeyVault/vaults",
"roleDefinitionIds": [
"/providers/Microsoft.Authorization/roleDefinitions/f25e0fa2-a7c8-4377-a976-54943a77a395"
],
"existenceCondition": {
"field": "Microsoft.KeyVault/vaults/networkAcls.defaultAction",
"equals": "Deny"
},
"deployment": {
"properties": {
"mode": "incremental",
"template": {
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"keyvaultname": {
"type": "string"
},
"locationname": {
"type": "string"
},
"skuname": {
"type": "string"
},
"accessPoliciesname": {
"type": "array"
}
},
"resources": [
{
"name": "[parameters('keyvaultname')]",
"location": "[parameters('locationname')]",
"type": "Microsoft.KeyVault/vaults",
"apiVersion": "2018-02-14",
"properties": {
"tenantId": "be42d65b-eb64-4a64-8aa3-ae47eef3af3e",
"accessPolicies": "[parameters('accessPoliciesname')]",
"sku": {
"name": "[parameters('skuname')]",
"family": "A"
},
"networkAcls": {
"defaultAction": "Deny",
"bypass": "None",
"ipRules": [
{
"value": "1.2.3.0/27"
},
{
"value": "1.5.6.0/24"
}
]
}
}
}
]
},
"parameters": {
"keyvaultname": {
"value": "[field('name')]"
},
"locationname": {
"value": "[field('location')]"
},
"skuname": {
"value": "[field('Microsoft.KeyVault/vaults/sku.name')]"
},
"accessPoliciesname": {
"value": "[field('Microsoft.KeyVault/vaults/accessPolicies')]"
}
}
}
},
"name": "[field('name')]"
}
}
}
}
}
I would recommend checking your ARM template to make sure that it is correct. Sometimes when you use the export a template function the ARM template might not work without testing. If you have an issue with the ARM template, I would direct your questions to them