Implementing Azure Policy using Terraform

Published May 28 2020 06:37 AM 10.7K Views

Use Case:

Terraform is a tool that could help us to create infrastructure using the configuration files. The infrastructure could later be updated with change in execution plan. It can be used as a tool for carrying out continuous  deployments for various Azure Resources .Azure Policy is a governance service to keep our environments in consistent shape and exercise control.

In this blog post, we would be understanding the way we can configure Terraform to create and assign Azure policies as well as remediate existing policies.

Configuring Terraform :

Terraform can be configured in any of the following ways :

  1. Inbuilt setup in Azure Cloud Shell
  2. Local Terraform Engine
  3. Market Place image for setting up Terraform on IaaS VM.

For this setup, we would be using Local Terraform engine to carry out the deployments. Steps to setup a local terraform engine can be found here : https://docs.microsoft.com/en-us/azure/developer/terraform/install-configure

Considering Azure Policy, Terraform provides 4 different modules for different purpose. To find the arguments supported please refer inline links for the Terraform modules.

  1. azurerm_policy_assignment : To create Policy Assignments using Terraform.

LINk : https://www.terraform.io/docs/providers/azurerm/r/policy_assignment.html

  1. azurerm_policy_definition : To create Policy Definitions using Terraform.

LINK : https://www.terraform.io/docs/providers/azurerm/r/policy_definition.html

  1. azurerm_policy_remediation : to create remediation tasks for the policy assignment.

LINK : https://www.terraform.io/docs/providers/azurerm/r/policy_remediation.html

  1. azurerm_policy_set_definition : To create policy initiatives.

LINK : https://www.terraform.io/docs/providers/azurerm/r/policy_set_definition.html

We would need to reference the modules as per the need in our Terraform code. For this blog, we would be creating a policy definition using an inbuilt policy and then creating an assignment as well as a remediation task for the policy.

Once downloaded in local directory, Terraform configuration would look like following :

folder structure.jpg

We can use cmd to run the terraform engine so as to initiate the deployments. Out of all the files, terraform.log file can come handy to troubleshoot deployment failures from debug traces logged in the file.

For this example, we would be using two .tf files for terraform deployment. Azure.tf to setup the variables and Antimalware.tf to setup policies.

Lifecycle of Terraform Deployment :

Terraform deployment can be structured into 3 steps namely init, plan and apply,

Terraform init: This would initialize the environment for local terraform engine so as to initiate the deployment. azurerm version and other

details are setup during this phase.

terraform plan: This is one of the most popular step where in terraform plans the blueprint for the deployment to occur. Majority of template

errors are ruled out in this step. This is rather useful since user can correct the code before the deployment starts.

Terraform apply : Once the plan has been saved, user can go ahead and start the deployment process.

Managing Permissions when using service principal:

Whenever Terraform is set to use a service principal, please ensure that the service principal provided has resource policy contributor rights for the policy assignment to work.

You can check for the name of service principal using the command Get-AzureADObjectbyObjectId : https://docs.microsoft.com/en-us/powershell/module/azuread/get-azureadobjectbyobjectid?view=azureadp... .

If access is not provided, you might face a 403 unauthorized error while trying to configure policies. Here is a snip from the cmd :

unauthorized.jpg

 

Looking for the ADobject with the Object ID we can get the name of the service principal that would be required for checking permissions.

ADObject.jpg

For assigning policies, this service principal should have Resource Policy Contributor access over the subscription.

RUNNING THROUGH THE TERRAFORM FILES :

We had been using Inbuilt Policy in this example that would deploy Antimalware extension for the servers. This would also need remediation and managed identity to be set in assignment.

Contents of Azure.tf file :

provider "azurerm" {

  version = "=2.3.0"

  subscription_id = "<enter subscription ID>"

features {}

}

You can create variables in this file that you can later use in your terraform deployment. The version 2.3.0 of azurerm would be loaded into the session when terraform init command is run.

Contents of AntiMalware.tf file :

resource "azurerm_policy_definition" "IaaSAntiMalwarePolicy" {

  name         = "IaaSAntiMalwarePolicy"

  policy_type  = "Custom"

  mode         = "All"

  display_name = "my-policy-definition"

 

  policy_rule = <<POLICY_RULE

   {

      "if": {

        "allOf": [

          {

            "field": "type",

            "equals": "Microsoft.Compute/virtualMachines"

          },

          {

            "field": "Microsoft.Compute/imagePublisher",

            "equals": "MicrosoftWindowsServer"

          },

          {

            "field": "Microsoft.Compute/imageOffer",

            "equals": "WindowsServer"

          },

          {

            "field": "Microsoft.Compute/imageSKU",

            "in": [

              "2008-R2-SP1",

              "2008-R2-SP1-smalldisk",

              "2012-Datacenter",

              "2012-Datacenter-smalldisk",

              "2012-R2-Datacenter",

              "2012-R2-Datacenter-smalldisk",

              "2016-Datacenter",

              "2016-Datacenter-Server-Core",

              "2016-Datacenter-Server-Core-smalldisk",

              "2016-Datacenter-smalldisk",

              "2016-Datacenter-with-Containers",

              "2016-Datacenter-with-RDSH",

              "2019-Datacenter",

              "2019-Datacenter-Core",

              "2019-Datacenter-Core-smalldisk",

              "2019-Datacenter-Core-with-Containers",

              "2019-Datacenter-Core-with-Containers-smalldisk",

              "2019-Datacenter-smalldisk",

              "2019-Datacenter-with-Containers",

              "2019-Datacenter-with-Containers-smalldisk"

            ]

          }

        ]

      },

      "then": {

        "effect": "deployIfNotExists",

        "details": {

          "type": "Microsoft.Compute/virtualMachines/extensions",

          "existenceCondition": {

            "allOf": [

              {

                "field": "Microsoft.Compute/virtualMachines/extensions/type",

                "equals": "IaaSAntimalware"

              },

              {

                "field": "Microsoft.Compute/virtualMachines/extensions/publisher",

                "equals": "Microsoft.Azure.Security"

              }

            ]

          },

          "roleDefinitionIds": [

            "/providers/microsoft.authorization/roleDefinitions/9980e02c-c2be-4d73-94e8-173b1dc7cf3c"

          ],

          "deployment": {

            "properties": {

              "mode": "incremental",

              "template": {

                "$schema": "http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",

                "contentVersion": "1.0.0.0",

                "parameters": {

                  "vmName": {

                    "type": "string"

                  },

                  "location": {

                    "type": "string"

                  },

                  "ExclusionsPaths": {

                    "type": "string",

                    "defaultValue": "",

                    "metadata": {

                      "description": "Semicolon delimited list of file paths or locations to exclude from scanning"

                    }

                  },

                  "ExclusionsExtensions": {

                    "type": "string",

                    "defaultValue": "",

                    "metadata": {

                      "description": "Semicolon delimited list of file extensions to exclude from scanning"

                    }

                  },

                  "ExclusionsProcesses": {

                    "type": "string",

                    "defaultValue": "",

                    "metadata": {

                      "description": "Semicolon delimited list of process names to exclude from scanning"

                    }

                  },

                  "RealtimeProtectionEnabled": {

                    "type": "string",

                    "defaultValue": "true",

                    "metadata": {

                      "description": "Indicates whether or not real time protection is enabled (default is true)"

                    }

                  },

                  "ScheduledScanSettingsIsEnabled": {

                    "type": "string",

                    "defaultValue": "false",

                    "metadata": {

                      "description": "Indicates whether or not custom scheduled scan settings are enabled (default is false)"

                    }

                  },

                  "ScheduledScanSettingsScanType": {

                    "type": "string",

                    "defaultValue": "Quick",

                    "metadata": {

                      "description": "Indicates whether scheduled scan setting type is set to Quick or Full (default is Quick)"

                    }

                  },

                  "ScheduledScanSettingsDay": {

                    "type": "string",

                    "defaultValue": "7",

                    "metadata": {

                      "description": "Day of the week for scheduled scan (1-Sunday, 2-Monday, ..., 7-Saturday)"

                    }

                  },

                  "ScheduledScanSettingsTime": {

                    "type": "string",

                    "defaultValue": "120",

                    "metadata": {

                      "description": "When to perform the scheduled scan, measured in minutes from midnight (0-1440). For example: 0 = 12AM, 60 = 1AM, 120 = 2AM."

                    }

                  }

                },

                "resources": [

                  {

                    "name": "[concat(parameters('vmName'),'/IaaSAntimalware')]",

                    "type": "Microsoft.Compute/virtualMachines/extensions",

                    "location": "[parameters('location')]",

                    "apiVersion": "2017-12-01",

                    "properties": {

                      "publisher": "Microsoft.Azure.Security",

                      "type": "IaaSAntimalware",

                      "typeHandlerVersion": "1.3",

                      "autoUpgradeMinorVersion": true,

                      "settings": {

                        "AntimalwareEnabled": true,

                        "RealtimeProtectionEnabled": "[parameters('RealtimeProtectionEnabled')]",

                        "ScheduledScanSettings": {

                          "isEnabled": "[parameters('ScheduledScanSettingsIsEnabled')]",

                          "day": "[parameters('ScheduledScanSettingsDay')]",

                          "time": "[parameters('ScheduledScanSettingsTime')]",

                          "scanType": "[parameters('ScheduledScanSettingsScanType')]"

                        },

                        "Exclusions": {

                          "Extensions": "[parameters('ExclusionsExtensions')]",

                          "Paths": "[parameters('ExclusionsPaths')]",

                          "Processes": "[parameters('ExclusionsProcesses')]"

                        }

                      }

                    }

                  }

                ]

              },

              "parameters": {

                "vmName": {

                  "value": "[field('name')]"

                },

                "location": {

                  "value": "[field('location')]"

                },

                "RealtimeProtectionEnabled": {

                  "value": "true"

                },

                "ScheduledScanSettingsIsEnabled": {

                  "value": "true"

                }

              }

            }

          }

        }

      }

    }

POLICY_RULE

 

  parameters = <<PARAMETERS

{

}   

PARAMETERS

}

 

resource "azurerm_policy_assignment" "IaaSAntiMalwarePolicyAssignment" {

  name                 = "IaaSAntiMalwarePolicyAssignment"

  scope                =  “/subscriptions/00000000-0000-0000-000000000000””

  policy_definition_id = azurerm_policy_definition.IaaSAntiMalwarePolicy.id

  description          = "Policy Assignment created via an Acceptance Test"

  display_name         = "IaaSAntiMalwarePolicyAssignment"

 

  parameters = <<PARAMETERS

{

 

}

PARAMETERS

location="eastus"

identity {

 

type="SystemAssigned"

}

}

 

resource "azurerm_policy_remediation" "antimalware" {

  name                 = "remeditationforantimalwarepolicy"

  scope                = azurerm_policy_assignment.IaaSAntiMalwarePolicyAssignment.scope

  policy_assignment_id = azurerm_policy_assignment.IaaSAntiMalwarePolicyAssignment.id

 

}

 

Breaking the code into small pieces, the first section would create a policy definition with the name : my-policy-definition

Since there are no parameters, thus the PARAMETERS section has been kept blank. Here is the snip :

policydef.jpg

Terraform plan would layout the execution plan and would list down all the resources that would be provisioned. In this case we would be configuring 3 resources- policy definition, policy assignment and remediation task.

Second section of Terraform code would create a policy assignment using the terraform module. We have setup the identity section in assignment so as to setup managed identity through terraform. Location Parameter is needed for the managed identity.

Third section would be creating a remediation task on the policy assignment scope. Since we are doing it in one deployment there would not be any resources remediated by the time this policy would be created ( state would be not started). Here is snip from terraform window after final deployment :

assignment.jpg

Looking at Azure Portal , we can see the resources to be created :

Policy Definition :

portaldef.jpg

Policy Assignment :

portalassignment.jpg

Remediation Task :

remediation.jpg

This way, we can use the sections separately in our environment so as to automate the work of assigning policies or creating remediation tasks using Terraform.

Happy Learning!

Version history
Last update:
‎Sep 15 2020 08:07 AM
Updated by: