Azure Citadel
  • Blogs

  • ARM
  • Azure Arc
    • Overview
    • Azure Arc-enabled Servers
      • Prereqs
      • Scenario
      • Hack Overview
      • Azure Landing Zone
      • Arc Pilot resource group
      • Azure Monitoring Agent
      • Additional policy assignments
      • Access your on prem VMs
      • Create onboarding scripts
      • Onboarding using scripts
      • Inventory
      • Monitoring
      • SSH
      • Windows Admin Center
      • Governance
      • Custom Script Extension
      • Key Vault Extension
      • Managed Identity
    • Azure Arc-enabled Kubernetes
      • Prereqs
      • Background
      • Deploy Cluster
      • Connect to Arc
      • Enable GitOps
      • Deploy Application
      • Enable Azure AD
      • Enforce Policy
      • Enable Monitoring
      • Enable Azure Defender
      • Enable Data Services
      • Enable Application Delivery
    • Useful Links
  • Azure CLI
    • Install
    • Get started
    • JMESPATH queries
    • Integrate with Bash
  • Azure Landing Zones
    • Prereqs
    • Day 1
      • Azure Baristas
      • Day 1 Challenge
    • Day 2
      • Example
      • Day 2 Challenge
    • Day 3
      • Day 3 Challenge
    • Useful Links
  • Azure Policy
    • Azure Policy Basics
      • Policy Basics in the Azure Portal
      • Creating Policy via the CLI
      • Deploy If Not Exists
      • Management Groups and Initiatives
    • Creating Custom Policies
      • Customer scenario
      • Policy Aliases
      • Determine the logic
      • Create the custom policy
      • Define, assign and test
  • Azure Stack HCI
    • Overview
    • Useful Links
    • Updates from Microsoft Ignite 2022
  • Marketplace
    • Introduction
      • Terminology
      • Offer Types
    • Partner Center
    • Offer Type
    • Publish a VM Offer HOL
      • Getting Started
      • Create VM Image
      • Test VM Image
      • VM Offer with SIG
      • VM Offer with SAS
      • Publish Offer
    • Other VM Resources
    • Publish a Solution Template HOL
      • Getting Started
      • Create ARM Template
      • Validate ARM Template
      • Create UI Definition
      • Package Assets
      • Publish Offer
    • Publish a Managed App HOL
      • Getting Started
      • Create ARM Template
      • Validate ARM Template
      • Create UI Definition
      • Package Assets
      • Publish Offer
    • Managed Apps with AKS HOL
    • Other Managed App Resources
    • SaaS Offer HOLs
    • SaaS Offer Video Series
      • Video 1 - SaaS Offer Overview
      • Video 2 - Purchasing a SaaS Offer
      • Video 3 - Purchasing a Private SaaS Plan
      • Video 4 - Publishing a SaaS Offer
      • Video 5 - Publishing a Private SaaS Plan
      • Video 6 - SaaS Offer Technical Overview
      • Video 7 - Azure AD Application Registrations
      • Video 8 - Using the SaaS Offer REST Fulfillment API
      • Video 9 - The SaaS Client Library for .NET
      • Video 10 - Building a Simple SaaS Landing Page in .NET
      • Video 11 - Building a Simple SaaS Publisher Portal in .NET
      • Video 12 - SaaS Webhook Overview
      • Video 13 - Implementing a Simple SaaS Webhook in .NET
      • Video 14 - Securing a Simple SaaS Webhook in .NET
      • Video 15 - SaaS Metered Billing Overview
      • Video 16 - The SaaS Metered Billing API with REST
  • Microsoft Fabric
    • Theory
    • Prereqs
    • Fabric Capacity
    • Set up a Remote State
    • Create a repo from a GitHub template
    • Configure an app reg for development
    • Initial Terraform workflow
    • Expanding your config
    • Configure a workload identity
    • GitHub Actions for Microsoft Fabric
    • GitLab pipeline for Microsoft Fabric
  • Packer & Ansible
    • Packer
    • Ansible
    • Dynamic Inventories
    • Playbooks & Roles
    • Custom Roles
    • Shared Image Gallery
  • Partner
    • Lighthouse and Partner Admin Link
      • Microsoft Cloud Partner Program
      • Combining Lighthouse and PAL
      • Minimal Lighthouse definition
      • Using service principals
      • Privileged Identity Management
    • Useful Links
  • REST API
    • REST API theory
    • Using az rest
  • Setup
  • Terraform
    • Fundamentals
      • Initialise
      • Format
      • Validate
      • Plan
      • Apply
      • Adding resources
      • Locals and outputs
      • Managing state
      • Importing resources
      • Destroy
    • Working Environments for Terraform
      • Cloud Shell
      • macOS
      • Windows with PowerShell
      • Windows with Ubuntu in WSL2
    • Using AzAPI
      • Using the REST API
      • azapi_resource
      • Removing azapi_resource
      • azapi_update_resource
      • Data sources and outputs
      • Removing azapi_update_resource
  • Virtual Machines
    • Azure Bastion with native tools & AAD
    • Managed Identities

  • About
  • Archive
  1. Home
  2. Azure Policy
  3. Creating Custom Policies
  4. Determine the logic

Table of Contents

  • Introduction
  • Which resourceTypes?
  • Source Address set to Any?
  • Destination Port Ranges?
  • Logical operators
  • Splat aliases
  • Count
  • Next steps

Determine the logic

You can't create a policy without knowing the logic that you need to embed in the policy.

Introduction

OK, lots of options which could result in a very complex policy if were had to deal with all the possible permutations. Having some knowledge of how the Azure Resource Manager works can help reduce that complexity. Let’s work through it.

Which resourceTypes?

We have to test the networkSecurityGroup/securityRules resourceType. The JIT process will create a single security rule against the existing NSG.

But we should also test the aliases at both the networkSecurityGroup resourceType. Our policy should also prevent the creation or update of an NSG with these securityRules within them.

Your rules should always include a "field": "type" rule so the system knows which resourceTypes to filter for testing.

{
  "field": "type",
  "equals": "Microsoft.Network/networkSecurityGroups/securityRules"
}

We’ll work through the additional test conditions for the Microsoft.Network/networkSecurityGroups/securityRules resourceType before moving on to Microsoft.Network/networkSecurityGroups.

Source Address set to Any?

The sourceAddressPrefix property will have a string value. String values are supported by a wide range of conditions.

We will use "equals": "*" to test:

{
  "field": "Microsoft.Network/networkSecurityGroups/securityRules/sourceAddressPrefix",
  "equals": "*"
}

Do we also need to test the sourceAddressPrefixes or sourceAddressPrefixes[*] aliases? The answer is no.

The Azure Resource Manager layer prevents ‘*’ being combined with other values in a sourceAddressPrefixes range. If you specify sourceAddressPrefixes with ‘*’ by itself (a single element array) then sourceAddressPrefix will be set to ‘*’ and sourceAddressPrefixes will be set to an empty array.

Sometimes you have to test a few variants to understand the behaviour.

Destination Port Ranges?

We need to test that the destination port ranges does not equal any of our Just In Time ports. The most likely are 22 and 3389, although the WinRM ports could also be requested.

The JIT request API call only allows a single destination port range value to be entered so we can forget the destinationPortRanges and destinationPortRanges[*] aliases and just test the destinationPortRange.

{
  "field": "Microsoft.Network/networkSecurityGroups/securityRules/destinationPortRange",
  "in": [
    "22",
    "3389",
    "5985",
    "5986"
  ]
}

Logical operators

There are three logical operators that you can use when nesting JSON for more complex conditions

  • not
  • allOf
  • anyOf

You can nest allOf within not to create noneOf.

At the networkSecurityGroups/securityRules resourceType level we need all three conditions to be true, plus the direction:Inbound and access:Allow properties, so the JSON would look like this:

{
  "allOf": [
    {
      "field": "type",
      "equals": "Microsoft.Network/networkSecurityGroups/securityRules"
    },
    {
      "field": "Microsoft.Network/networkSecurityGroups/securityRules/direction",
      "equals": "Inbound"
    },
    {
      "field": "Microsoft.Network/networkSecurityGroups/securityRules/access",
      "equals": "Allow"
    },
    {
      "field": "Microsoft.Network/networkSecurityGroups/securityRules/sourceAddressPrefix",
      "equals": "*"
    },
    {
      "field": "Microsoft.Network/networkSecurityGroups/securityRules/destinationPortRange",
      "in": [
        "22",
        "3389",
        "5985",
        "5986"
      ]
    }
  ]
}

We will use a parameter for that array of ports to make the policy more flexible.

OK, that is the networkSecurityGroups/securityRules resourceType done.

Splat aliases

Now for the networkSecurityGroups resourceType. Here are the matching aliases:

  • Microsoft.Network/networkSecurityGroups/securityRules[*].sourceAddressPrefix
  • Microsoft.Network/networkSecurityGroups/securityRules[*].destinationPortRange

There is good documentation to help you understand the [*] alias. I’ll call them splat aliases as they are similar to the splat expressions in Terraform.

With splat aliases there is an implicit loop so that you can use the string evaluators on each iteration. It is worth understanding the effect of the implicit AND between those iterations on the evaluations. Here are a few common variants:

  • All

    {
      "field": "Microsoft.Network/networkSecurityGroups/securityRules[*].sourceAddressPrefix",
      "equals": "*"
    }
    
  • None

    {
      "not": {
        "field": "Microsoft.Network/networkSecurityGroups/securityRules[*].sourceAddressPrefix",
        "equals": "*"
      }
    }
    
  • Any

    {
      "not": {
        "field": "Microsoft.Network/networkSecurityGroups/securityRules[*].sourceAddressPrefix",
        "notEquals": "*"
      }
    }
    

    If you see not and notEquals combined then it will be on a splat alias.

Count

We will need to do a compound test of all of the conditions whilst cycling through the splat aliases, and we want a true result if any of the security rules in an NSG tick all of the boxes. This is where count comes in. Count can be used to see how many of the iterations meet the condition criteria, so you can check for none or all or any, or for an exact number.

Therefore, here is the JSON for the ‘networkSecurityGroups’ level.

{
  "allOf": [
    {
      "field": "type",
      "equals": "Microsoft.Network/networkSecurityGroups"
    },
    {
      "count": {
        "field": "Microsoft.Network/networkSecurityGroups/securityRules[*]",
        "where": {
          "allOf": [
            {
              "field": "Microsoft.Network/networkSecurityGroups/securityRules[*].direction",
              "equals": "Inbound"
            },
            {
              "field": "Microsoft.Network/networkSecurityGroups/securityRules[*].access",
              "equals": "Allow"
            },
            {
              "field": "Microsoft.Network/networkSecurityGroups/securityRules[*].sourceAddressPrefix",
              "equals": "*"
            },
            {
              "field": "Microsoft.Network/networkSecurityGroups/securityRules[*].destinationPortRange",
              "in": "[parameters('ports')]"
            }
          ]
        }
      },
      "greater": 0
    }
  ]
}

Next steps

OK, we have everything we need. Let’s start building the custom policy.

Policy Aliases Determine the logic Create the custom policy