DeployIfNotExists Security Rule to NSG

Charlotte Edwards 11 Reputation points Microsoft Employee
2021-04-12T09:32:34.463+00:00

I have been trying to create a policy definition to deploy a security rule to NSGs should it not already exist. I have managed to get this working using the modify effect but ideally would like to have it working using deployifnotexists.

I am able to create and assign the policy however the remediation step always fails with the error "Policy evaluation exceeded the maximum allowed time." Any help greatly appreciated.

{
    "type": "Microsoft.Authorization/policyDefinitions",
    "properties": {
        "displayName": "Append NSG Rule",
        "description": "This Policy will append a rule to newly deployed NSGs.",
        "mode": "All",
        "parameters": {
            "name": {
                "type": "String",
                "metadata": {
                    "displayName": "Rule Name",
                    "description": "This is the name of the security rule itself."
                }
            },
            "protocol": {
                "type": "String",
                "metadata": {
                    "displayName": "protocol",
                    "description": "Network protocol this rule applies to. - Tcp, Udp, Icmp, Esp, *, Ah"
                }
            },
            "sourcePortRange": {
                "type": "Array",
                "metadata": {
                    "displayName": "sourcePortRange",
                    "description": "The source port or range. Integer or range between 0 and 65535. Asterisk '*' can also be used to match all ports."
                }
            },
            "destinationPortRange": {
                "type": "Array",
                "metadata": {
                    "displayName": "destinationPortRange",
                    "description": "The destination port or range. Integer or range between 0 and 65535. Asterisk '*' can also be used to match all ports."
                }
            },
            "sourceAddressPrefix": {
                "type": "Array",
                "metadata": {
                    "displayName": "sourceAddressPrefix",
                    "description": "The CIDR or source IP range. Asterisk '*' can also be used to match all source IPs. Default tags such as 'VirtualNetwork', 'AzureLoadBalancer' and 'Internet' can also be used. If this is an ingress rule, specifies where network traffic originates from."
                }
            },
            "destinationAddressPrefix": {
                "type": "Array",
                "metadata": {
                    "displayName": "destinationAddressPrefix",
                    "description": "The destination address prefix. CIDR or destination IP range. Asterisk '*' can also be used to match all source IPs. Default tags such as 'VirtualNetwork', 'AzureLoadBalancer' and 'Internet' can also be used."
                }
            },
            "access": {
                "type": "String",
                "metadata": {
                    "displayName": "access",
                    "description": "The network traffic is allowed or denied. - Allow or Deny"
                }
            },
            "priority": {
                "type": "Integer",
                "metadata": {
                    "displayName": "priority",
                    "description": "The priority of the rule. The value can be between 100 and 4096. The priority number must be unique for each rule in the collection. The lower the priority number, the higher the priority of the rule."
                }
            },
            "direction": {
                "type": "String",
                "metadata": {
                    "displayName": "direction",
                    "description": "The direction of the rule. The direction specifies if rule will be evaluated on incoming or outgoing traffic. - Inbound or Outbound"
                }
            }
        },
        "policyRule": {
            "if": {
                "field": "type",
                "equals": "Microsoft.Network/networkSecurityGroups"
            },
            "then": {
                "effect": "DeployIfNotExists",
                "details": {
                    "type": "Microsoft.Network/networkSecurityGroups",
                    "roleDefinitionIds": [
                        "/providers/Microsoft.Authorization/roleDefinitions/8e3af657-a8ff-443c-a75c-2fe8c4bcb635"
                    ],
                    "existencecondition": {
                        "allOf": [{
                                "field": "type",
                                "equals": "Microsoft.Network/networkSecurityGroups"
                            },
                            {
                                "count": {
                                    "field": "Microsoft.Network/networkSecurityGroups/securityRules[*]",
                                    "where": {
                                        "allOf": [{
                                                "field": "Microsoft.Network/networkSecurityGroups/securityRules[*].name",
                                                "equals": "[parameters('name')]"
                                            },
                                            {
                                                "field": "Microsoft.Network/networkSecurityGroups/securityRules[*].protocol",
                                                "equals": "[parameters('protocol')]"
                                            },
                                            {
                                                "anyOf": [{
                                                        "value": "[equals(field('Microsoft.Network/networkSecurityGroups/securityRules[*].sourcePortRange'), parameters('sourcePortRange'))]",
                                                        "equals": true
                                                    },
                                                    {
                                                        "value": "[equals(field('Microsoft.Network/networkSecurityGroups/securityRules[*].sourcePortRanges'), parameters('sourcePortRange'))]",
                                                        "equals": true
                                                    }
                                                ]
                                            },
                                            {
                                                "anyOf": [{
                                                        "value": "[equals(field('Microsoft.Network/networkSecurityGroups/securityRules[*].destinationPortRange'), parameters('destinationPortRange'))]",
                                                        "equals": true
                                                    },
                                                    {
                                                        "value": "[equals(field('Microsoft.Network/networkSecurityGroups/securityRules[*].destinationPortRanges'), parameters('destinationPortRange'))]",
                                                        "equals": true
                                                    }
                                                ]
                                            },
                                            {
                                                "anyOf": [{
                                                        "value": "[equals(field('Microsoft.Network/networkSecurityGroups/securityRules[*].sourceAddressPrefix'), parameters('sourceAddressPrefix'))]",
                                                        "equals": true
                                                    },
                                                    {
                                                        "value": "[equals(field('Microsoft.Network/networkSecurityGroups/securityRules[*].sourceAddressPrefixes'), parameters('sourceAddressPrefix'))]",
                                                        "equals": true
                                                    }
                                                ]
                                            },
                                            {
                                                "anyOf": [{
                                                        "value": "[equals(field('Microsoft.Network/networkSecurityGroups/securityRules[*].destinationAddressPrefix'), parameters('destinationAddressPrefix'))]",
                                                        "equals": true
                                                    },
                                                    {
                                                        "value": "[equals(field('Microsoft.Network/networkSecurityGroups/securityRules[*].destinationAddressPrefixes'), parameters('destinationAddressPrefix'))]",
                                                        "equals": true
                                                    }
                                                ]
                                            },
                                            {
                                                "field": "Microsoft.Network/networkSecurityGroups/securityRules[*].access",
                                                "equals": "[parameters('access')]"
                                            },
                                            {
                                                "field": "Microsoft.Network/networkSecurityGroups/securityRules[*].priority",
                                                "equals": "[parameters('priority')]"
                                            },
                                            {
                                                "field": "Microsoft.Network/networkSecurityGroups/securityRules[*].direction",
                                                "equals": "[parameters('direction')]"
                                            }
                                        ]
                                    }
                                },
                                "equals": 0
                            }
                        ]
                    },
                    "deployment": {
                        "properties": {
                            "mode": "incremental",
                            "parameters": {
                                "name": {
                                    "value": "[parameters('name')]"
                                },
                                "protocol": {
                                    "value": "[parameters('protocol')]"
                                },
                                "sourcePortRange": {
                                    "value": "[parameters('sourcePortRange')]"
                                },
                                "destinationPortRange": {
                                    "value": "[parameters('destinationPortRange')]"
                                },
                                "sourceAddressPrefix": {
                                    "value": "[parameters('sourceAddressPrefix')]"
                                },
                                "destinationAddressPrefix": {
                                    "value": "[parameters('destinationAddressPrefix')]"
                                },
                                "access": {
                                    "value": "[parameters('access')]"
                                },
                                "priority": {
                                    "value": "[parameters('priority')]"
                                },
                                "direction": {
                                    "value": "[parameters('direction')]"
                                }
                            },
                            "template": {
                                "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
                                "contentVersion": "1.0.0.0",
                                "parameters": {
                                    "name": {
                                        "type": "String"
                                    },
                                    "protocol": {
                                        "type": "String"
                                    },
                                    "sourcePortRange": {
                                        "type": "Array"
                                    },
                                    "destinationPortRange": {
                                        "type": "Array"
                                    },
                                    "sourceAddressPrefix": {
                                        "type": "Array"
                                    },
                                    "destinationAddressPrefix": {
                                        "type": "Array"
                                    },
                                    "access": {
                                        "type": "String"
                                    },
                                    "priority": {
                                        "type": "Integer"
                                    },
                                    "direction": {
                                        "type": "String"
                                    }
                                },
                                "resources": [{
                                    "name": "[parameters('name')]",
                                    "type": "Microsoft.Network/networkSecurityGroups/securityRules",
                                    "apiVersion": "2020-07-01",
                                    "properties": {
                                        "protocol": "[parameters('protocol')]",
                                        "sourcePortRange": "[if(equals(length(parameters('sourcePortRange')), 1), parameters('sourcePortRange')[0], json('null'))]",
                                        "sourcePortRanges": "[if(greater(length(parameters('sourcePortRange')), 1), parameters('sourcePortRange'), json('null'))]",
                                        "destinationPortRange": "[if(equals(length(parameters('destinationPortRange')), 1), parameters('destinationPortRange')[0], json('null'))]",
                                        "destinationPortRanges": "[if(greater(length(parameters('destinationPortRange')), 1), parameters('destinationPortRange'), json('null'))]",
                                        "sourceAddressPrefix": "[if(equals(length(parameters('sourceAddressPrefix')), 1), parameters('sourceAddressPrefix')[0], json('null'))]",
                                        "sourceAddressPrefixes": "[if(greater(length(parameters('sourceAddressPrefix')), 1), parameters('sourceAddressPrefix'), json('null'))]",
                                        "destinationAddressPrefix": "[if(equals(length(parameters('destinationAddressPrefix')), 1), parameters('destinationAddressPrefix')[0], json('null'))]",
                                        "destinationAddressPrefixes": "[if(greater(length(parameters('destinationAddressPrefix')), 1), parameters('destinationAddressPrefix'), json('null'))]",
                                        "access": "[parameters('access')]",
                                        "priority": "[parameters('priority')]",
                                        "direction": "[parameters('direction')]"
                                    }
                                }]
                            }
                        }
                    }
                }
            }
        }
    }
}
Azure Virtual Network
Azure Virtual Network
An Azure networking service that is used to provision private networks and optionally to connect to on-premises datacenters.
2,762 questions
{count} votes

2 answers

Sort by: Most helpful
  1. Charlotte Edwards 11 Reputation points Microsoft Employee
    2021-11-12T16:44:41.927+00:00

    Hi,

    I managed a solution for this however it was not policy based in the end.

    For a deploy if not exists effect, it is necessary to specify the name of the NSG in the ARM template to push out the required rule.

    In my case the name of the NSG needed to be dynamic rather than a hard coded parameter.

    I ended up using an event driven solution with an event grid triggering a PowerShell function.

    I configured the event grid to pick up on NSG creation events, these events then triggered the function to deploy the nsg rule.

    To prevent the deletion of the NSG rule it can be locked with a read only lock.

    Hope this helps

    Charlotte


  2. Moritz Mann 6 Reputation points
    2022-01-07T08:46:08.54+00:00

    Hello Charlotte,
    would you be okay sharing the Logic App Template for the hardcoded NSG name?
    It would be super helpful for Thomas and myself.
    Thank you!
    Kind regards, Moritz

    0 comments No comments

Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.