
To integrate Azure Policy with Terraform, you'll need to use the Azure Provider in Terraform, which allows you to manage Azure resources, including policies.
The Azure Provider for Terraform is a plugin that enables Terraform to interact with Azure services, including Azure Policy. It's a must-have for managing Azure resources with Terraform.
To use the Azure Provider, you'll need to install it using the Terraform CLI, and then configure it with your Azure credentials. This will allow Terraform to authenticate with Azure and manage your resources.
The Azure Provider supports a wide range of Azure services, including Azure Policy, which enables you to define and enforce policies across your Azure resources.
Azure Policy with Terraform
Azure Policy with Terraform is a powerful combination that allows you to define and enforce policies across your Azure resources. You can use Terraform to create and manage Azure Policy definitions, assignments, and exemptions.
To get started, you'll need to configure the AzureRM provider in your Terraform configuration. This is done by setting up the provider and specifying the required version. For example, you can use the following code in your main.tf file: provider "azurerm" { features {} } terraform { required_providers { azurerm = { source = "hashicorp/azurerm" version = ">= 2.96.0" } } }
Once you've configured the provider, you can create a policy definition using the azurerm_policy_definition resource. This resource has four required arguments: name, display_name, mode, and policy_type. For example, you can create a policy definition to enforce a naming convention for storage accounts: resource "azurerm_policy_definition" "StorageAccountNamingConvention" { name = "StorageAccountNamingConvention" display_name = "Storage Accounts should follow naming convention" mode = "Indexed" policy_type = "Custom" }
You can also use Terraform to create policy assignments at different scopes, such as management groups, subscriptions, or resource groups. For example, you can create a policy assignment at a management group using the azurerm_management_group_policy_assignment resource: resource "azurerm_management_group_policy_assignment" "auditvms" { name = "audit-vm-manageddisks" management_group_id = var.cust_scope policy_definition_id = "/providers/Microsoft.Authorization/policyDefinitions/06a78e20-9358-41c9-923c-fb736d382a4d" description = "Shows all virtual machines not using managed disks" display_name = "Audit VMs without managed disks assignment" }
Here are the four top-level keys that you need to set when creating an initiative definition:
- name — unique name for the initiative.
- display_name — the name displayed with the initiative.
- description — a description of what the policy is for and does.
- policies — objects containing the value definitions of BuiltIn and Custom policies.
Create Plan
To create a plan for your Azure Policy with Terraform, you'll need to initialize Terraform and run a few commands. First, run the terraform init command to download the necessary providers for Azure. This command will get you set up with the Azure modules required to create the Azure resources in your Terraform configuration.
You'll also need to authenticate with Azure CLI for Terraform. This is done by running the az login command, which you can find more information on in the Azure Provider documentation.
Now you're ready to create the execution plan with the terraform plan command. To do this, run the terraform plan -out assignment.tfplan command, which will create a plan and save it to a file named assignment.tfplan. Note that there are security considerations to keep in mind when persisting execution plans.
Enforce Definitions with Terraform
You can enforce Azure Policy definitions via Terraform using the azurerm_subscription_policy_assignment resource. This allows you to assign built-in policy definitions to a subscription level scope.
To use azurerm_subscription_policy_assignment, you need to specify the policy definition ID, which can be found on the individual policy definition view in the Azure portal.
Policy assignment often requires a managed identity, either system- or user-assigned, with specific permissions to perform evaluation and remediation tasks.
Here are the key arguments for the azurerm_subscription_policy_assignment resource:
- name: The name of the policy assignment.
- subscription_id: The ID of the subscription where the policy will be enforced.
- policy_definition_id: The ID of the policy definition to be enforced.
- description: A description of the policy assignment.
- display_name: The display name of the policy assignment.
For example:
```terraform
resource "azurerm_subscription_policy_assignment" "auditvms" {
name = "audit-vm-manageddisks"
subscription_id = var.cust_scope
policy_definition_id = "/providers/Microsoft.Authorization/policyDefinitions/06a78e20-9358-41c9-923c-fb736d382a4d"
description = "Shows all virtual machines not using managed disks"
display_name = "Audit VMs without managed disks assignment"
}
```
You can also use other resources like azurerm_management_group_policy_assignment, azurerm_resource_group_policy_assignment, or azurerm_resource_policy_assignment to assign policies to different scopes.
Note that you can find more information about the azurerm_subscription_policy_assignment resource in the Azure provider for Terraform documentation.
Local Variables
In Azure Policy with Terraform, local variables are used to store data that can be used throughout the module.
We start by getting some random_uuid's setup for unique names of our policies, assignments, and exemptions. Some properties in the azurerm provider will auto-generate names for us, and others won't.
The initiative_definition_yaml needs to be decoded into a Terraform object that we can use throughout our module. This object is stored in the policies local variable for quick access.
Defining Azure Policy
Defining Azure Policy is a crucial step in managing and governing your Azure resources. You can define metadata for the policy definition, which includes a version and a category.
To define metadata, you use the Azure Policy JSON syntax inside the HashiCorp Terraform Language (HCL). This is done by placing the JSON code inside a jsonencode() function, which allows Terraform to interpret the JSON code and guarantee the correct syntax.
There are two types of metadata: version and category. The version is set to "1.0.0" and the category is set to "Storage". This is done by using the following code: "jsonencode({ metadata = { version = "1.0.0", category = "Storage" } })".
You can also define parameters used by the policy. In this example, there are two parameters: effectAction and namingPattern. The effectAction is a string type that defines what action the policy takes, and the namingPattern is a string that sets the naming pattern the storage account should follow.
Here are the two parameters:
- effectAction: The effect action is a string type that defines what action the policy takes. The two options are "Audit" or "Deny". Set the default value to "Audit."
- namingPattern: This parameter is a string and sets the naming pattern the storage account should follow. Use a question mark (?) for letters and a pound symbol (#) for numbers. Set the default value to "4sysops???####", which translates to the prefix "4sysops" followed by three letters and four numbers.
The policy rule is the core of the definition, as it describes the resource conditions to match and the effect to take. There are two conditions in this example: the resource type must be of type "Microsoft.Storage/storageAccounts" and the resource name does not match the value in the namingPattern parameter.
Here is the policy rule:
jsonencode({
policy_rule = {
if not all({
resourceTypeEquals('Microsoft.Storage/storageAccounts'),
not resourceNameEquals(namingPattern)
}) {
effect = { type = "Deny" }
}
}
})
You can also define an initiative definition, which is a collection of policy definitions. There are four top-level keys: name, display_name, description, and policies. The policies key contains objects containing the value definitions of BuiltIn and Custom policies.
Here are the four top-level keys:
- name — unique name for the initiative.
- display_name — the name displayed with the initiative.
- description — a description of what the policy is for and does.
- policies — objects containing the value definitions of BuiltIn and Custom policies.
When defining a Custom policy, you have a json definition of the Azure Policy. You can use string interpolation to set the effect on a per-environment basis.
Here is an example of a Custom policy definition:
jsonencode({
effect = {
type = "Deny"
}
})
This is how you can set the effect on a per-environment basis.
You can also create the policy definition using the azurerm_policy_definition resource type. This example policy enforces a naming convention for storage accounts. The policy definition has four required arguments: name, display_name, mode, and policy_type.
Here are the four required arguments:
- name: Policy definition short name ("StorageAccountNamingConvention").
- display_name: Policy definition display name ("Storage Accounts should follow naming convention").
- mode: Policy mode to specify which resource types Azure evaluates the policy against ("Indexed").
- policy_type: Policy type of "BuiltIn", "Custom", or "NotSpecified." This example uses "Custom."
Terraform Configuration
To create a Terraform configuration for Azure Policy, you'll need to set up the AzureRM provider. This is done by configuring the Azure provider in your Terraform configuration file, typically named `main.tf`. Here, you'll specify the required providers, including the AzureRM provider.
To create a Policy Assignment at a Management Group, Resource Group, or Subscription level, you'll use the `azurerm_management_group_policy_assignment`, `azurerm_resource_group_policy_assignment`, or `azurerm_subscription_policy_assignment` resource, respectively. For example, to create a Policy Assignment at a Subscription level, you can use the `azurerm_subscription_policy_assignment` resource.
Here are the steps to create the Terraform configuration, variable, and output files:
1. Create a new folder named `policy-assignment` and change directories into it.
2. Create a `main.tf` file with the following code:
```terraform
provider "azurerm" {
features {}
}
terraform {
required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = ">= 2.96.0"
}
}
}
resource "azurerm_subscription_policy_assignment" "auditvms" {
name = "audit-vm-manageddisks"
subscription_id = var.cust_scope
policy_definition_id = "/providers/Microsoft.Authorization/policyDefinitions/06a78e20-9358-41c9-923c-fb736d382a4d"
description = "Shows all virtual machines not using managed disks"
display_name = "Audit VMs without managed disks assignment"
}
```
3. Create a `variables.tf` file with the following code:
```terraform
variable "cust_scope" {
default = "{scope}"
}
```
4. Create an `output.tf` file with the following code:
```terraform
output "assignment_id" {
value = azurerm_subscription_policy_assignment.auditvms.id
}
```
Note that you'll need to replace `{scope}` with a valid scope value, such as a management group ID or a resource group ID.
Azure Policy Remediation
Azure Policy Remediation allows you to automatically fix non-compliant resources through tasks that can be executed on those resources in order to make them compliant with the policy.
Azure Policy definitions with deployIfNotExists or modify effects support remediation, which represents a collection of tasks to make resources compliant.
You can create a remediation task to automatically fix non-compliant resources, for example, through Infrastructure as Code (IaC) with Terraform.
To create a remediation task in a different scope, you can use azurerm_management_group_policy_remediation, azurerm_resource_group_policy_remediation or azurerm_resource_policy_remediation accordingly.
You can check the task execution status in the Remediation -> Remediation tasks section of the Azure Policy page.
Exemption
Policy exemptions can be tricky to manage, but Terraform makes it easier by allowing us to match zero or more exemptions to the correct assignment.
We reference the correct Terraform resource block given each assignment type (mg, sub, rg) has its own Terraform resource, using local variables to reference a resource rather than a string.
Terraform will try to evaluate each of these even if they're not called, which would be fine except that they will never all exist at the same time given assignment can only be done on a single scope.
The one function ensures that the assignment_id key will only ever return one value, which is what we want to prevent errors.
We validate that the assignment.scope is correct at this stage, which is crucial to ensure the exemption is applied correctly.
The name property is constructed out of the random_uuid for the exemptions as well as the last component of the resource ID, which is used to generate the id or key field on our for_each.
Remediation
Remediation is a game-changer when it comes to Azure Policy. You can create a remediation task to automatically fix non-compliant resources, for example through IaC with Terraform.
With a DeployIfNotExists effect, you can enforce a policy definition and then create a remediation task to fix non-compliant resources. This can be done in a different scope using azurerm_management_group_policy_remediation, azurerm_resource_group_policy_remediation, or azurerm_resource_policy_remediation.
You can identify non-compliant resources by running a command with the assignment_id returned by terraform apply. This will output the resource IDs of non-compliant resources into a JSON file.
To view the non-compliant resources, you can use the Azure portal or run a command to get the resource IDs. The results will resemble what you'd typically see listed under Non-compliant resources in the Azure portal view.
To remove the assignment created, you can use Azure CLI or reverse the Terraform execution plan with terraform destroy. This will delete the assignment or destroy the Terraform plan.
Terraform and Azure Policy Integration
To enable Azure Policy add-on with AzureRM provider for Terraform, you can use the terraform argument azure_policy_enabled = true.
You can assign Azure Policy definitions via Terraform using the azurerm_subscription_policy_assignment resource, which allows you to assign policies on the subscription level.
Using a SystemAssigned identity is a simple option for policy assignment, but it's generally recommended to use UserAssigned managed identities for better security.
In Terraform, policy assignment requires a managed identity with specific permissions to perform evaluation and remediation tasks.
Sources
- https://kristhecodingunicorn.com/post/aks-azure-policy/
- https://brendanthompson.com/manage-azure-policy-with-terraform/
- https://purple.telstra.com/blog/azure-policy-as-code-with-terraform-part-1
- https://4sysops.com/archives/manage-azure-policy-using-terraform/
- https://learn.microsoft.com/en-us/azure/governance/policy/assign-policy-terraform
Featured Images: pexels.com