Azure Policy's new DenyAction effect
Tags: azure
Published: 2023-01-03 16:38:07


A new Azure Policy effect is [Generally available](https://techcommunity.microsoft.com/t5/azure-governance-and-management/generally-available-secure-critical-infrastructure-from/ba-p/3938639), DenyAction. 
This new effect only supports the delete action. This is good news for people who didn’t manage to secure access to shared resources, like spoke VNET or other important resources. We have no way to protect them against deletion.

Imagine you work for a company where Hub/Spoke model is used to provide connectivity to multiple subscriptions. In this architecture, you deploy a spoke VNET and a peering to the hub VNET inside your users’ subscriptions. The problem, your users may have the owner role of their subscriptions, they can deploy whatever they want but they can also delete whatever they want, including the VNET used to support the connectivity to the central HUB. How to deploy a policy to protect this special resource. 
For this example let's say that all the spoke VNETs are called spoke-vnet. 

You need to know that the denyAction effect can only safeguard against deletion resources that support tags and location. For that, the mode of the Azure Policy should be "indexed".

Then we need to identify the target we want to protect:

```json
{
	"mode": "Indexed",
    "parameters": {
		"vnetName": {
		  "type": "String",
		  "metadata": {
			"displayName": "vnetName to protect"
		  },
		  "defaultValue": "spoke-vnet"
		}
	  },
``` 

We target virtual networks with spoke-vnet as a name. The name is in a parameter in case we want to reuse the policy against another virtual network. 

Then we must define the action section. But before we need to understand a little more about how the DenyAction work. The spoke-vnet VNET could be deleted from several levels. 

We target virtual networks with spoke-vnet as a name. The name is in a parameter in case we want to reuse the policy against another virtual network. 

Then we must define the action section. But before we need to understand a little more about how the DenyAction work. The spoke-vnet VNET could be deleted from several levels. It could be deleted as a resource when a user clicks on delete in the portal at the resource level, for example. 
But there are two other use cases when the deletion is initiated at an higger level when the subscription or the parent resource group is deleted. If the subscription is deleted, there will be no protection, and the resource will be deleted. But if the resource group is deleted, we have more options, we can let the resource be deleted when its resource group is deleted, or we can choose to deny the action and disallow the deletion of the resource group. 
This can be done using the "cascadeBehaviors" optional property. It includes only one property, "resourceGroup" and two allowed values "allow" and "deny" (default value)
So if we want to protect the VNET against accidental deletion while still want to keep the possibility to delete the parent resource group we could use this: 

```json 
		"then": {
			"effect": "DenyAction",
			"details": {
				"actionNames": [
					"delete"
				],
				"cascadeBehaviors": {
					"resourceGroup": "allow"
				}
			}
		}
``` 

The whole policy is here:

```json
{
	"mode": "Indexed",
    "parameters": {
		"vnetName": {
		  "type": "String",
		  "metadata": {
			"displayName": "vnetName to protect"
		  },
		  "defaultValue": "spoke-vnet"
		}
	  },
	"policyRule": {
		"if": {
			"allOf": [
				{
					"field": "type",
					"equals": "Microsoft.Network/virtualNetworks"
				},
				{
					"field": "name",
					"equals": "[parameters('vnetName')]"
				}
			]
		},
		"then": {
			"effect": "DenyAction",
			"details": {
				"actionNames": [
					"delete"
				],
				"cascadeBehaviors": {
					"resourceGroup": "allow"
				}
			}
		}
	}
}
```

Now we can create this policy in Azure and assign it to a subscription or a management group. You will need to wait a few minutes before it can be fully operational. The compliance state should be compliant (if not, resources will not be protected). 

![Compliance Status in Azure Portal](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cqpi0e2r6rfmts4tjmrz.png)

After you make sure that the state is compliant you can try to delete a VNET called spoke-vnet in Azure Portal, you should get this:


![Error in Azure Portal](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wc16hh1nfdmsfvdfbln4.png)

If you try to delete the VNET in PowerShell 

```powershell
Remove-AzVirtualNetwork -ResourceGroupName <RgName> -Name spoke-vnet -Force
```

You should have this error:

```
Remove-AzVirtualNetwork: Deletion of resource 'spoke-vnet' was disallowed by policy. Policy identifiers: '[{"policyAssignment":{"name":"deny-action-spoke","id":"/subscriptions/xxxx/providers/Microsoft.Authorization/policyAssignments/e74c5c38db944024afef5fa2"},"policyDefinition":{"name":"deny-action-spoke","id":"/providers/Microsoft.Management/managementGroups/c986548e-494d-4f3a-b716-42287a39531b/providers/Microsoft.Authorization/policyDefinitions/6023d344-5146-43a4-a7f8-dee408517fe2"}},{"policyAssignment":{"name":"deny-action-spoke-assign","id":"/subscriptions/XXXXX/providers/Microsoft.Authorization/policyAssignments/9d327e170e204455a7afe18c"},"policyDefinition":{"name":"deny-action-spoke","id":"/providers/Microsoft.Management/managementGroups/c986548e-494d-4f3a-b716-42287a39531b/providers/Microsoft.Authorization/policyDefinitions/6023d344-5146-43a4-a7f8-dee408517fe2"}}]'.
StatusCode: 403
ReasonPhrase: Forbidden
ErrorCode: RequestDisallowedByPolicy
Target: spoke-vnet
ErrorMessage: ...
```

You can see that the Azure ARM API returns a 403 Forbidden HTTP error when trying to delete directly the resource. 

But what happens if we try to delete the parent resource group? You remember in the "cascadeBehaviors" property, we select allow for resourceGroup. In this case, the delete action is authorized on the VNET and the resource group is deleted. 

In short, this new DeleteAction can be useful to protect resources in a subscription when lock can not be used, but it has some limitations. You can only protect resources that support tags and location, this excludes objects like VNET peering, or VM extensions. You can set up protection for the resource group deletion but it is not possible to protect a resource if the subscription is deleted.