This repository contains some Rego policy files designed for Azure, both AzureRM and AzAPI. The policy files are structured as follows:
To use these policies, you can use the Conftest tool. You can use the following command to run the policies against your Terraform plan:
conftest test --all-namespaces --update git::https://github.com/Azure/policy-library-avm.git//policy <path-to-tfplan>
To generate a Terraform plan file:
terraform plan -out=tfplan.binary && terraform show -json tfplan.binary > tfplan.json
Or you can use this library against the brown field infrastructure:
terraform show -json > state.json
conftest test --all-namespaces --update git::https://github.com/Azure/policy-library-avm.git//policy state.json
Microsoft.Compute/virtualMachines
legacy_virtual_machine_not_allowed
migrate_vm_using_availability_sets_to_vmss_flex
mission_critical_virtual_machine_should_use_premium_or_ultra_disks
mission_critical_virtual_machine_should_use_zone
Microsoft.ContainerService/managedClusters
configure_aks_default_node_pool_zones
Microsoft.DocumentDB/databaseAccounts
configure_cosmosdb_account_continuous_backup_mode
Microsoft.Network/applicationGateways
migrate_to_application_gateway_v2
deploy_application_gateway_in_a_zone_redundant_configuration
Microsoft.Network/loadBalancers
use_nat_gateway_instead_of_outbound_rules_for_production_load_lalancer
use_resilient_load_lalancer_sku
Microsoft.Network/publicIPAddresses
public_ip_use_standard_sku_and_zone_redundant_ip
Microsoft.Network/virtualNetworkGateways
virtual_network_gateway_use_zone_redundant_sku
Microsoft.DBforMySQL/flexibleServers
mysql_flexible_server_high_availability_mode_zone_redundant
mysql_flexible_server_geo_redundant_backup_enabled
Microsoft.DBforPostgreSQL/flexibleServers
postgresql_flexible_server_geo_redundant_backup_enabled
postgresql_flexible_server_high_availability_mode_zone_redundant
Microsoft.Storage/storageAccounts
storage_accounts_are_zone_or_region_redundant
To apply a subset of policies, you can specify the policy folders you want to apply, e.g.:
conftest test --all-namespaces --update git::https://github.com/Azure/policy-library-avm.git//policy/Azure-Proactive-Resiliency-Library-v2 <path-to-tfplan>
This will only apply the policies under Azure-Proactive-Resiliency-Library-v2
.
To skip a subset of policies, you can create an exception rego file, e.g.:
package Azure_Proactive_Resiliency_Library_v2
import rego.v1
exception contains rules if {
rules = ["use_nat_gateway_instead_of_outbound_rules_for_production_load_lalancer", "storage_accounts_are_zone_or_region_redundant"]
}
Save it to exception.rego
, then you can apply the exception file with the policies:
conftest test --all-namespaces --update git::https://github.com/Azure/policy-library-avm.git//policy/Azure-Proactive-Resiliency-Library-v2 -p policy -p exception.rego <path-to-tfplan>
All contribution are welcome, please follow the structure below:
.
├── common
├── ruleset1
│ ├── provider1
│ └── provider2
└── ruleset2
├── provider1
└── provider2
The policy files are grouped by ruleset, then provider. Now azurerm
policies should be further grouped by service folder as terraform-provider-azurerm
.
All shared util code MUST be stored in common
folder.
Each rego file MUST has a corresponding xxx.mock.json
file. The mock JSON file should contain a top-level key named "mock", which maps to a dictionary. This dictionary can have keys "valid" and "invalid", each mapping to another dictionary of test cases.
Example structure for mock JSON files:
{
"mock": {
"valid": {
"case1": {...},
"case2": {...}
},
"invalid": {
"case1": {...},
"case2": {...}
}
}
}
Alternatively, you can put all cases under the mock
key directly:
{
"mock": {
"case1": {...},
"invalid_case2": {...}
}
}
Any keys other than valid
and invalid
would be treated as a single case, any single cases without invalid prefix would be considered as a valid case.
To contribute a new policy, you MUST provide at least one valid case.
All policies MUST support both azurerm
and azapi
providers.
Please do:
deny_migrate_to_application_gateway_v2 contains reason if {
resource := data.utils.resource(input, "azurerm_application_gateway")[_]
not valid_azurerm_sku(resource)
reason := sprintf("Azure-Proactive-Resiliency-Library-v2: '%s' `azurerm_application_gateway` must have 'sku.name' set to 'Standard_v2' or 'WAF_v2': https://azure.github.io/Azure-Proactive-Resiliency-Library-v2/azure-resources/Network/applicationGateways/#migrate-to-application-gateway-v2", [resource.address])
}
Please DO NOT:
deny contains reason if {
resource := data.utils.resource(input, "azurerm_application_gateway")[_]
not valid_azurerm_sku(resource)
reason := sprintf("Azure-Proactive-Resiliency-Library-v2: '%s' `azurerm_application_gateway` must have 'sku.name' set to 'Standard_v2' or 'WAF_v2': https://azure.github.io/Azure-Proactive-Resiliency-Library-v2/azure-resources/Network/applicationGateways/#migrate-to-application-gateway-v2", [resource.address])
}
These rule names could be used in exceptions
so users could skip the check for specific resources.
Please do:
package Azure_Proactive_Resiliency_Library_v2.configure_cosmosdb_account_continuous_backup_mode
import rego.v1
valid_azurerm_cosmosdb_account_backup_policy_type(resource) if {
resource.values.backup[_].type == "Continuous"
}
deny_configure_cosmosdb_account_continuous_backup_mode contains reason if {
resource := data.utils.resource(input, "azurerm_cosmosdb_account")[_]
not valid_azurerm_cosmosdb_account_backup_policy_type(resource)
reason := sprintf("Azure-Proactive-Resiliency-Library-v2: '%s' `azurerm_cosmosdb_account` must have backup type configured to 'Continuous': https://azure.github.io/Azure-Proactive-Resiliency-Library-v2/azure-resources/DocumentDB/databaseAccounts/#configure-continuous-backup-mode", [resource.address])
}
Since we have rules for both azurerm
and azapi
providers, we need a predictable way to add a rule into exception list. Assuming we have the same rule for azapi
resource:
package Azure_Proactive_Resiliency_Library_v2.configure_cosmosdb_account_continuous_backup_mode
import rego.v1
valid_azapi_cosmosdb_account_backup_policy_type(resource) if {
resource.values.body.properties.backupPolicy.type == "Continuous"
}
deny_configure_cosmosdb_account_continuous_backup_mode contains reason if {
resource := data.utils.resource(input, "azapi_resource")[_]
data.utils.is_azure_type(resource.values, "Microsoft.DocumentDB/databaseAccounts")
not valid_azapi_cosmosdb_account_backup_policy_type(resource)
reason := sprintf("Azure-Proactive-Resiliency-Library-v2: '%s' `azapi_resource` must have backup type configured to 'Continuous': https://azure.github.io/Azure-Proactive-Resiliency-Library-v2/azure-resources/DocumentDB/databaseAccounts/#configure-continuous-backup-mode", [resource.address])
}
To ignore rule configure_cosmosdb_account_continuous_backup_mode
, we need a new rego file:
package Azure_Proactive_Resiliency_Library_v2.configure_cosmosdb_account_continuous_backup_mode
import rego.v1
exception contains rules if {
rules = ["configure_cosmosdb_account_continuous_backup_mode"]
}
As we are using rule name as package name suffix, we need to make sure the helper function name is unique. Please use the helper function name unique, the provider name could help here:
valid_azapi_cosmosdb_account_backup_policy_type(resource) if {
resource.values.body.properties.backupPolicy.type == "Continuous"
}
According to the HashiCorp's OPA policies document:
The run data contains information like workspace details and the organization name. To access the properties from the Terraform plan data in your policies, use
input.plan
. To access properties from the Terraform run, useinput.run
.
Unlike Terraform plan file, the actual plan on HCP Terraform are wrapped in input.plan
, so you MUST use resource := data.utils.resource(input, "azurerm_postgresql_flexible_server")[_]
to get the actual plan object.
Please update the README file to include the new policy in #Supported Policies section.