/terraform-azurerm-policy-as-code

Terraform modules that simplify the workflow of custom and built-in Azure Policies

Primary LanguageHCLMIT LicenseMIT

Terraform-Azure-Policy-as-Code

Azure Policy as Code with Terraform

MIT License TF Registry
Open in Visual Studio Code
CD Tests CI Tests
Go to topic discussions

Repo Folder Structure

📦examples
  ├──📜assignments_mg.tf
  ├──📜backend.tf
  ├──📜data.tf
  ├──📜definitions.tf
  ├──📜exemptions.tf
  ├──📜initiatives.tf
  ├──📜variables.tf
📦modules
  └──📂def_assignment
      ├──📜main.tf
      ├──📜outputs.tf
      └──📜variables.tf
  └──📂definition
      ├──📜main.tf
      ├──📜outputs.tf
      └──📜variables.tf
  └──📂exemption
      ├──📜main.tf
      ├──📜outputs.tf
      └──📜variables.tf
  └──📂initiative
      ├──📜main.tf
      ├──📜outputs.tf
      └──📜variables.tf
  └──📂set_assignment
      ├──📜main.tf
      ├──📜outputs.tf
      └──📜variables.tf
📦policies
  └──📂policy_category (e.g. General, should correspond to [var.policy_category])
      └──📜policy_name.json (e.g. whitelist_regions, should correspond to [var.policy_name])
📦scripts
  ├──📂dsc_examples
  ├──📜build_guest_config_packages.ps1 (build and publish azure policy guest configuration packages)
  └──📜convert_to_v2.ps1 (converts policies to version 2 of the repo library)

Custom Policy Definitions Module

This module depends on populating var.policy_name and var.policy_category to correspond with the respective custom policy definition json file found in the local library. You can also parse in other template files and data sources at runtime, see the definition module readme for examples and acceptable inputs.

module whitelist_regions {
  source              = "gettek/policy-as-code/azurerm//modules/definition"
  version             = "2.6.1"
  policy_name         = "whitelist_regions"
  display_name        = "Allow resources only in whitelisted regions"
  policy_category     = "General"
  management_group_id = data.azurerm_management_group.org.id
}

📘 Microsoft Docs: Azure Policy definition structure

Policy Initiative (Set Definitions) Module

Policy Initiatives are used to combine sets of definitions in order to simplify their assignment

module platform_baseline_initiative {
  source                  = "gettek/policy-as-code/azurerm//modules/initiative"
  version                 = "2.6.1"
  initiative_name         = "platform_baseline_initiative"
  initiative_display_name = "[Platform]: Baseline Policy Set"
  initiative_description  = "Collection of policies representing the baseline platform requirements"
  initiative_category     = "General"
  management_group_id     = data.azurerm_management_group.org.id

  member_definitions = [
    module.whitelist_resources.definition,
    module.whitelist_regions.definition
  ]
}

⚠️ Warning: If any two member_definition_ids contain the same parameters then they will be merged by this module (except for "effect" when setting merge_effects = false) as seen here. In most cases this is beneficial but if unique values are required it may be best practice to set unique keys directly within your custom definition.json files such as [parameters('listOfResourceTypesAllowed_WhitelistResources')] instead of [parameters('listOfResourceTypesAllowed')].

📘 Microsoft Docs: Azure Policy initiative definition structure

Policy Definition Assignment Module

module org_mg_whitelist_regions {
  source            = "gettek/policy-as-code/azurerm//modules/def_assignment"
  version           = "2.6.1"
  definition        = module.whitelist_regions.definition
  assignment_scope  = data.azurerm_management_group.org.id
  assignment_effect = "Deny"

  assignment_parameters = {
    "listOfRegionsAllowed" = [
      "UK South",
      "UK West",
      "Global"
    ]
  }
}

📘 Microsoft Docs: Azure Policy assignment structure

Policy Initiative Assignment Module

module org_mg_platform_diagnostics_initiative {
  source                  = "gettek/policy-as-code/azurerm//modules/set_assignment"
  version                 = "2.6.1"
  initiative              = module.platform_diagnostics_initiative.initiative
  assignment_scope        = data.azurerm_management_group.org.id
  assignment_effect       = "DeployIfNotExists"
  skip_remediation        = false
  skip_role_assignment    = false
  remediation_scope       = data.azurerm_subscription.current.id
  resource_discovery_mode = "ReEvaluateCompliance"

  assignment_parameters = {
    workspaceId                 = data.azurerm_log_analytics_workspace.workspace.id
    storageAccountId            = data.azurerm_storage_account.sa.id
    eventHubName                = data.azurerm_eventhub_namespace.ehn.name
    eventHubAuthorizationRuleId = data.azurerm_eventhub_namespace_authorization_rule.ehnar.id
    metricsEnabled              = "True"
    logsEnabled                 = "True"
  }

  assignment_not_scopes = [
    data.azurerm_management_group.team_a.id
  ]

  non_compliance_messages = {
    null                                        = "The Default non-compliance message for all member definitions"
    "DeployApplicationGatewayDiagnosticSetting" = "The non-compliance message for the deploy_application_gateway_diagnostic_setting definition"
  }
}

Policy Exemption Module

Use the exemption module in favour of not_scopes to create an auditable time-sensitive Policy exemption

module exemption_team_a_mg_deny_nic_public_ip {
  source               = "gettek/policy-as-code/azurerm//modules/exemption"
  name                 = "Deny NIC Public IP Exemption"
  display_name         = "Exempted while testing"
  description          = "Allows NIC Public IPs for testing"
  scope                = data.azurerm_management_group.team_a.id
  policy_assignment_id = module.team_a_mg_deny_nic_public_ip.id
  exemption_category   = "Waiver"
  expires_on           = "2023-05-25" # optional

  # optional
  metadata = {
    requested_by  = "Team A"
    approved_by   = "Mr Smith"
    approved_date = "2021-11-30"
    ticket_ref    = "1923"
  }
}

📘 Microsoft Docs: Azure Policy exemption structure

Achieving Continuous Compliance

⚙️Assignment Effects

Azure Policy supports the following types of effect:

Types Policy Effects from least to most restrictive

💡 Note: If you're managing tags, it's recommended to use Modify instead of Append as Modify provides additional operation types and the ability to remediate existing resources. However, Append is recommended if you aren't able to create a managed identity or Modify doesn't yet support the alias for the resource property.

📘 Microsoft Docs: Understand how effects work

👥Role Assignments

Role assignments and remediation tasks will be automatically created if the Policy Definition contains a list of Role Definitions. You can override these with explicit ones, as seen here, or specify skip_role_assignment=true to omit creation. By default these will scope at the policy assignment but can be changed by setting role_assignment_scope.

✅Remediation Tasks

Unless you specify skip_remediation=true, the *_assignment modules will automatically create remediation tasks for policies containing effects of DeployIfNotExists and Modify. The task name is suffixed with a timestamp() to ensure a new one gets created on each terraform apply.

⏱️On-demand evaluation scan

To trigger an on-demand compliance scan with terraform, set resource_discovery_mode=ReEvaluateCompliance on *_assignment modules, defaults to ExistingNonCompliant.

💡 Note: ReEvaluateCompliance only applies to remediation at Subscription scope and below and will take longer depending on the size of your environment.

🎯Definition and Assignment Scopes

  • Should be Defined as high up in the hierarchy as possible.
  • Should be Assigned as low down in the hierarchy as possible.
  • assignment_not_scopes such as child resource groups, individual resources or entire subscriptions, can be specified as enforcement exemptions.
  • Policy overrides RBAC so even Subscription owners fall under the same compliance enforcements assigned at a higher scope (unless assigned at subscription scope).

Policy Definition and Assignment Scopes

⚠️ Requirement: Ensure the deployment account has at least Resource Policy Contributor role at the definition_scope and assignment_scope. To successfully create Role-assignments (or group memberships) the same account may also require the User Access Administrator role at the assignment_scope or preferably the definition_scope to simplify workflows.

📗Useful Resources

Limitations

  • DefinitionName has a maximum length of 64 characters and AssignmentName a maximum length of 24 characters
  • DisplayName has a maximum length of 128 characters and description a maximum length of 512 characters
  • There's a maximum count for each object type for Azure Policy. For definitions, an entry of Scope means the management group or subscription. For assignments and exemptions, an entry of Scope means the management group, subscription, resource group, or individual resource:
Where What Maximum count
Scope Policy definitions 500
Scope Initiative definitions 200
Tenant Initiative definitions 2,500
Scope Policy or initiative assignments 200
Scope Exemptions 1,000
Policy definition Parameters 20
Initiative definition Policies 1,000
Initiative definition Parameters 300
Policy or initiative assignments Exclusions (notScopes) 400
Policy rule Nested conditionals 512
Remediation task Resources 50,000
Policy definition, initiative, or assignment request body Bytes 1,048,576