Featured image of post Day 55: Terraform ESLZ Policies

Day 55: Terraform ESLZ Policies

A guide to assist with configuring for your environment.

Welcome to this overview on customizing ESLZ Policies using Terraform.

Customizing Terraform Landing Zones Policies

We can play about with policies that are deployed in our environment by looking at the “.terraform\modules\enterprise_scale\modules\archetypes\lib\policy_definitions” folder, these .JSON files can be referenced by our “.terraform\modules\enterprise_scale\modules\archetypes\lib\archetype_definitions” .JSON files.

An example would be the custom policy below, as per the description this policy would (by default) deny the creation of public IPs.

{
  "name": "Deny-PublicIP",
  "type": "Microsoft.Authorization/policyDefinitions",
  "apiVersion": "2021-06-01",
  "scope": null,
  "properties": {
    "policyType": "Custom",
    "mode": "Indexed",
    "displayName": "Deny the creation of public IP",
    "description": "This policy denies creation of Public IPs under the assigned scope.",
    "metadata": {
      "version": "1.0.0",
      "category": "Network"
    },
    "parameters": {
      "effect": {
        "type": "String",
        "allowedValues": [
          "Audit",
          "Deny",
          "Disabled"
        ],
        "defaultValue": "Deny",
        "metadata": {
          "displayName": "Effect",
          "description": "Enable or disable the execution of the policy"
        }
      }
    },
    "policyRule": {
      "if": {
        "field": "type",
        "equals": "Microsoft.Network/publicIPAddresses"
      },
      "then": {
        "effect": "[parameters('effect')]"
      }
    }
  }
}

An interesting observation I made was the correlation to the following built-in policy:

{
  "properties": {
    "displayName": "Network interfaces should not have public IPs",
    "policyType": "BuiltIn",
    "mode": "Indexed",
    "description": "This policy denies the network interfaces which are configured with any public IP. Public IP addresses allow internet resources to communicate inbound to Azure resources, and Azure resources to communicate outbound to the internet. This should be reviewed by the network security team.",
    "metadata": {
      "version": "1.0.0",
      "category": "Network"
    },
    "parameters": {},
    "policyRule": {
      "if": {
        "allOf": [
          {
            "field": "type",
            "equals": "Microsoft.Network/networkInterfaces"
          },
          {
            "not": {
              "field": "Microsoft.Network/networkInterfaces/ipconfigurations[*].publicIpAddress.id",
              "notLike": "*"
            }
          }
        ]
      },
      "then": {
        "effect": "deny"
      }
    }
  },
  "id": "/providers/Microsoft.Authorization/policyDefinitions/83a86a26-fd1f-447c-b59d-e51f44264114",
  "type": "Microsoft.Authorization/policyDefinitions",
  "name": "83a86a26-fd1f-447c-b59d-e51f44264114"

One denies the ability to create public IPs under “Microsoft.Network/publicIPAddresses” and the other “Microsoft.Network/networkInterfaces” denies it on a resource type level. The custom policy allows to lock down certain subscriptions where you would not want users to have the ability to create public IPs, whereas the built-in policy (not enabled by default) has the effect of “deny” only if applied to a subscription, effectively denying the selected subscription from assigning public IPs directly to a NIC. So you can see how similar policies can interact with each other. In the creation of a new environment I would probably look at the defaults from Microsoft, and decide which ones I wish to deploy and then look to fine tune where possible with custom policies, and the terraform module to a degree gives us quite a few examples to play with and understand the structure of the formatting to deploy our own settings if we wanted.

If you navigate to https://docs.microsoft.com/en-us/azure/governance/policy/samples/built-in-policies it will show you all the “BuiltIn” policies, it’s worth taking a look and if you wanted to export them all you could run the following:

$Policy_Definition = az policy definition list | Out-File '.\Policy_Definitions.csv'

How to add policies

Inside “.terraform\modules\enterprise_scale\modules\archetypes\lib\archetype_definitions\archetype_definition_es_identity.tmpl.json” we can see the following below, which are all custom policies.

    "es_identity": {
        "policy_assignments": [
            "Deny-Public-IP",
            "Deny-RDP-From-Internet",
            "Deny-Subnet-Without-Nsg",
            "Deploy-VM-Backup"],
        "policy_definitions": [],
        "policy_set_definitions": [],
        "role_definitions": [],
        "archetype_config": {
            "parameters": {},
            "access_control": {}
        }
    }
}

If we wanted to add in another custom definition we could just add an extra Deny etc into the policy_assignments parameter.

We can also nest the policies, so for example in “.terraform\modules\enterprise_scale\modules\archetypes\lib\archetype_definitions\archetype_definition_es_management.tmpl.json” we could add policies at a higher level to have them invoked on this group and below.

{
    "es_management": {
        "policy_assignments": [
            "Deploy-Log-Analytics"
        ],
        "policy_definitions": [],
        "policy_set_definitions": [],
        "role_definitions": [],
        "archetype_config": {
            "parameters": {},
            "access_control": {}
        }
    }
}

You can also define policy definitions, sets and roles by dropping files into “.terraform\modules\enterprise_scale\modules\archetypes\lib\policy_set_definitions” and “.terraform\modules\enterprise_scale\modules\archetypes\lib\role_definitions”. It’s interesting that some policies in here are actually built=in policies that have been added in here manually, they reference the policies within Azure already but there’s still this manual step it seems of having to copy the definition details from Azure into the “.terraform\modules\enterprise_scale\modules\archetypes\lib\policy_definitions” folder etc. Something to look into a little more.