Problem
An Azure policy blocks a terraform apply operation, but the same resource management action succeeds when performed directly in the Azure Portal.
Prerequisites
- You use the AzureRM provider for Terraform runs.
- You have defined a policy in Azure to restrict certain resource configurations.
Cause
The AzureRM provider interacts with the Azure API, which may behave differently than the Azure Portal UI. The provider often makes several sequential API calls to create or update a single resource as defined in your Terraform configuration.
This multi-step process can result in intermediate resource states with unexpected or default values that temporarily violate a policy, causing the terraform apply to fail, even if the final intended state would be compliant.
Solution
To resolve this, you must adjust your Azure policy to account for the intermediate API calls made by the AzureRM provider. You can identify the exact API payloads by running terraform apply with trace logging enabled.
Example 1: Action Group with an Empty smsReceivers Block
In this scenario, you use the azurerm_monitor_action_group resource and have a policy that blocks the use of smsReceivers.
Your policy may include a condition similar to this.
{
"field": "Microsoft.Insights/actiongroups/smsReceivers[*]",
"exists": true
}Although you have not defined an sms_receiver block in your Terraform configuration, the apply fails with a RequestDisallowedByPolicy error.
Error: creating or updating Action Group (Subscription: "****"
Resource Group Name: "myresourcegroup"
Action Group Name: "myactiongroup"): unexpected status 403 (403 Forbidden) with error: RequestDisallowedByPolicy: Resource 'myactiongroup' was disallowed by policy. Policy identifiers: '[{"policyAssignment":{"name":"Action Group allowed action types","id":"/subscriptions/****/resourceGroups/myresourcegroup/providers/Microsoft.Authorization/policyAssignments/bd56h0d5f6m8522fc7cyfhgh"},"policyDefinition":{"name":"Action Group allowed action types","id":"/subscriptions/****/providers/Microsoft.Authorization/policyDefinitions/9c513865-b4dc-4b42-ab1b-e8d5ad2364e6","version":"1.0.0"}},{"policyAssignment":{"name":"Action Group allowed notification types","id":"/subscriptions/****/providers/Microsoft.Authorization/policyAssignments/bc721c64cad7491f91153b7a"},"policyDefinition":{"name":"Action Group allowed notification types","id":"/subscriptions/****/providers/Microsoft.Authorization/policyDefinitions/134g949b-g222-5b75-9e2f-8cg23eg1cg62","version":"1.0.0"}}]'.
with azurerm_monitor_action_group.example,
on main.tf line 1, in resource "azurerm_monitor_action_group" "example":
1: resource "azurerm_monitor_action_group" "example" {By inspecting the trace log, you can find the API request payload sent by the provider.
{
"location": "northeurope",
"properties": {
"armRoleReceivers": [],
"automationRunbookReceivers": [],
"azureAppPushReceivers": [],
"azureFunctionReceivers": [],
"emailReceivers": [
{
"emailAddress": "example@example.com",
"name": "email***_-EmailAction-",
"useCommonAlertSchema": false
}
],
"enabled": true,
"eventHubReceivers": [
{
"eventHubName": "myeventhub",
"eventHubNameSpace": "myeventhub",
"name": "eventHUB",
"subscriptionId": "****",
"tenantId": "****",
"useCommonAlertSchema": true
}
],
"groupShortName": "myactiongroup",
"itsmReceivers": [],
"logicAppReceivers": [],
"smsReceivers": [],
"voiceReceivers": [],
"webhookReceivers": []
},
"tags": {}
}The payload includes an empty "smsReceivers": [] array. The provider sends this default empty block even though it was not defined in the configuration. To fix this, you must adjust your policy to permit an empty smsReceivers block.
Example 2: Kubernetes Cluster with a Delayed Policy Check
In this scenario, you use the azurerm_kubernetes_cluster resource and have a policy requiring that azurePolicy.enabled is set to true.
Your Terraform configuration includes the required setting.
azure_policy_enabled = true
Your policy blocks the request if the field does not exist or is not equal to true.
{
"field": "Microsoft.ContainerService/managedClusters/addonProfiles.azurePolicy.enabled",
"exists": false
},
{
"field": "Microsoft.ContainerService/managedClusters/addonProfiles.azurePolicy.enabled",
"notEquals": "true"
}A terraform apply fails with a RequestDisallowedByPolicy error.
Error: updating Kubernetes Cluster (Subscription: "***" Resource Group Name: "myresourcegroup" Kubernetes Cluster Name: "aks-cluster"): performing CreateOrUpdate: unexpected status 403 (403 Forbidden) with error: RequestDisallowedByPolicy: Resource 'aks-cluster' was disallowed by policy. ##...
An analysis of the trace log reveals that the provider creates the cluster using multiple API calls. The initial API call to create the cluster does not include the azurePolicy setting at all. A subsequent API call then enables the Azure Policy addon.
Because the first call lacks the azurePolicy.enabled field, it violates the policy. You must adjust the Azure policy to accommodate this multi-step provisioning behavior.