Policy as Code (PoC): Deploying and Managing Azure Policy in Terraform

What is Azure Policy ? As Per Official Microsoft Article


Understand evaluation outcomes

  • A resource is created or updated in a scope with a policy assignment.
  • A policy or initiative is newly assigned to a scope.
  • A policy or initiative already assigned to a scope is updated.
  • During the standard compliance evaluation cycle, which occurs once every 24 hours.

Control the response to an evaluation

  • Deny the resource change
  • Log the change to the resource
  • Alter the resource before the change
  • Alter the resource after the change
  • Deploy related compliant resources

Remediate non-compliant resources

Azure Policy vs Azure RBAC

Azure Policy Objects

Policy definition

Initiative definition


Going through the Terraform code

  • The main.tf creates the Policy Initiative.
  • The modules directory contains a generic policy definition template which every policy files calls.
  • The policies.auto.tfvars file contains values of all variables defined. You need to exchange “” with your values or change any values you deemed fit as per your requirement.

Understanding Policy code

backend "azurerm" {
resource_group_name = "<Your Value here>"
storage_account_name = "<Your Value here>"
container_name = "<Your Value here>"
key = "<Your Value here>"
module "allowed_regions_policy" {
source = "./modules/generic_policy_module"
policy_definition_name = "${var.org_prefix}${var.allowed_regions_policy_name}"
policy_definition_display_name = "${var.org_prefix}${var.allowed_regions_policy_display_name}"
policy_definition_description = var.allowed_regions_policy_description
policy_definition_metadata = jsonencode(
"category" : "${var.allowed_regions_policy_category}"
policy_definition_rule = jsonencode(
"if" : {
"allOf" : [
"field" : "location",
"notIn" : "${var.allowed_regions}"
"field" : "location",
"notEquals" : "global"
"field" : "type",
"notEquals" : "Microsoft.AzureActiveDirectory/b2cDirectories"
"then" : {
"effect" : "${var.allowed_regions_policy_effect}"
resource "azurerm_policy_definition" "policy" {
name = var.policy_definition_name
display_name = var.policy_definition_display_name
description = var.policy_definition_description
policy_type = "Custom"
mode = "All"
management_group_id = var.policy_management_scope
metadata = var.policy_definition_metadata
policy_rule = var.policy_definition_rule
parameters = var.policy_definition_parameters
resource "azurerm_policy_set_definition" "policy-set-definition" {
name = "${var.org_prefix}Baseline Azure Policy Set Definition"
policy_type = "Custom"
display_name = "${var.org_prefix}Baseline Azure Policy Set Definition"
management_group_id = var.policy_management_scope
policy_definition_reference {
policy_definition_id = module.allowed_regions_policy.policy_id
resource "azurerm_management_group_policy_assignment" "policy-assignment" {
name = "${var.org_prefix} Policy Initiative"
policy_definition_id = azurerm_policy_set_definition.policy-set-definition.id
management_group_id = var.initiative_management_scope
description = "This policy enables a set of definitions that can be deployed."
display_name = "${var.org_prefix} Baseline Azure Policy Initiative"
location = "canadacentral"
identity {
type = "SystemAssigned"
// Allowed Regions Policy Details
allowed_regions_policy_name = "Allowed Azure Regions"
allowed_regions_policy_display_name = "Allowed Azure Regions"
allowed_regions_policy_description = "This policy allows or audit resources to be created in the specific locations."
allowed_regions_policy_category = "General"
allowed_regions_policy_effect = "audit"

Policies that are created

  1. Allowed Regions
  2. Allowed Resource Types
  3. Allowed SQL Version
  4. Allowed Storage Account SKU
  5. Allowed VM Extensions
  6. Allowed Subnets for Public IP
  7. Allowed VM OS and version
  8. Allowed VM SKU
  9. DDOS Protection
  10. Diagnostics Settings
  11. Diagnostic settings logs to be send to Log Analytics WOrkspace
  12. Firewall Internet Traffic
  13. Key Vault Purge Protection
  14. Key Vault soft delete
  15. VM NIC IP Forwarding
  16. Enable Network Watchers
  17. NSG for every Subnet
  18. NSG Inbound rules
  19. SQL Database Private endpoint
  20. SQL Database TLS version
  21. SQL server public network access
  22. Storage Account Secure Transfer Settings
  23. Storage Account Key Expiry
  24. Storage Account Network Access
  25. Mandatory Tags
  26. Optional Tags
  27. Internet facing VM NSG
  28. VM managed disk
  29. VM management port
  30. VM encryption

Run the code

Authenticate Azure CLI

Trigger Manually




Love podcasts or audiobooks? Learn on the go with our new app.

Recommended from Medium

Goroutines and Channels Aren’t Free

Join Us for April’s Mambo Merge Office Hours!

An Amazing Tool to Manage Your APIs

QRcode Scanner (NEW APP)

Using Gradle to create a simple Scala Spark application

How to Do a Minimum Viable Feature Switch, Includes a Simple Code Example

Idempotent Kafka Consumer

Tips to Consider while Creating a World Class Mobile Testing Lab

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Arlan Nugara

Arlan Nugara

More from Medium

Project: Terraform Resource Creation and Management For Azure

Azure Landing Zone in Terraform-Complete CAF IaC Solution

How to Scale in Cloud with Landing Zones

💪Deploy VM-Series Next-Generation Firewall from Palo Alto Networks using Bicep Language