diff --git a/infrastructure/modules/policy/policy-assignments/main.tf b/infrastructure/modules/policy/policy-assignments/main.tf index fb1f23dd..52fb1019 100644 --- a/infrastructure/modules/policy/policy-assignments/main.tf +++ b/infrastructure/modules/policy/policy-assignments/main.tf @@ -1,6 +1,6 @@ # Configure a policy assignment for a specific resource resource "azurerm_resource_policy_assignment" "res_assignment" { - count = var.resource_id != "" && local.resource_id_type == "resource" ? 1 : 0 + count = var.resource_id != null && local.resource_id_type == "resource" ? 1 : 0 name = var.assignment_name resource_id = var.resource_id @@ -19,7 +19,7 @@ resource "azurerm_resource_policy_assignment" "res_assignment" { # Configure a policy assignment for a specific resource group resource "azurerm_resource_group_policy_assignment" "rg_assignment" { - count = var.resource_id != "" && local.resource_id_type == "resource-group" ? 1 : 0 + count = var.resource_id != null && local.resource_id_type == "resource-group" ? 1 : 0 name = var.assignment_name resource_group_id = var.resource_id @@ -38,7 +38,7 @@ resource "azurerm_resource_group_policy_assignment" "rg_assignment" { # Configure a policy assignment for a specific resource group resource "azurerm_subscription_policy_assignment" "sub_assignment" { - count = var.resource_id != "" && local.resource_id_type == "subscription" ? 1 : 0 + count = var.resource_id != null && local.resource_id_type == "subscription" ? 1 : 0 name = var.assignment_name subscription_id = var.resource_id @@ -46,6 +46,8 @@ resource "azurerm_subscription_policy_assignment" "sub_assignment" { enforce = var.enforce_policy location = var.policy_location + parameters = var.parameters != null ? jsonencode(var.parameters) : null + dynamic "identity" { for_each = var.requires_identity ? [1] : [] content { @@ -59,7 +61,7 @@ resource "azurerm_role_assignment" "role" { count = var.create_remediator_role ? 1 : 0 scope = var.policy_assignment_scope - principal_id = var.policy_assignment_principal_id + principal_id = var.policy_assignment_principal_id != null ? var.policy_assignment_principal_id : local.principal_id role_definition_name = "Resource Policy Contributor" condition = < [assignment\_name](#input\_assignment\_name) + +Description: A short name for this policy assignment + +Type: `string` + +### [display\_name](#input\_display\_name) + +Description: Policy assignment display name. + +Type: `string` + +### [policy\_assignment\_scope](#input\_policy\_assignment\_scope) + +Description: The scope at which this assignment is assigned + +Type: `string` + +### [policy\_definition\_id](#input\_policy\_definition\_id) + +Description: An identifier of a policy definition to assign. + +Type: `string` + +## Optional Inputs + +The following input variables are optional (have default values): + +### [create\_remediator\_role](#input\_create\_remediator\_role) + +Description: True to create an associated Policy Contributor role, false otherwise + +Type: `bool` + +Default: `false` + +### [description](#input\_description) + +Description: Description of the policy assignment. + +Type: `string` + +Default: `""` + +### [enabled\_log](#input\_enabled\_log) + +Description: Collection of logs to capture + +Type: `list(string)` + +Default: `[]` + +### [enabled\_metric](#input\_enabled\_metric) + +Description: Collection of metrics to capture + +Type: `list(string)` + +Default: `[]` + +### [enforce\_policy](#input\_enforce\_policy) + +Description: True if this policy should be enforced, false otherwise + +Type: `bool` + +Default: `true` + +### [log\_analytics\_wks\_id](#input\_log\_analytics\_wks\_id) + +Description: An identifier of a specific Log Analytics Workspace instance to use + +Type: `string` + +Default: `null` + +### [parameters](#input\_parameters) + +Description: Parameters for the policy assignment. + +Type: `map(any)` + +Default: `{}` + +### [policy\_assignment\_principal\_id](#input\_policy\_assignment\_principal\_id) + +Description: The identifier of a specific service principal to use for the policy assignment + +Type: `string` + +Default: `null` + +### [policy\_identities](#input\_policy\_identities) + +Description: A collection of principal identifiers assigned to this policy. Only required if the identity type is 'UserAssigned' + +Type: `list(string)` + +Default: `[]` + +### [policy\_location](#input\_policy\_location) + +Description: Defines where the policy will be deployed to. Required if identify is provided. + +Type: `string` + +Default: `"uksouth"` + +### [requires\_identity](#input\_requires\_identity) + +Description: True if the policy requires a managed identity, false otherwise + +Type: `bool` + +Default: `false` + +### [resource\_id](#input\_resource\_id) + +Description: An identifier of a specific resource to apply this policy onto + +Type: `string` + +Default: `null` +## Modules + +The following Modules are called: + +### [policy\_assignment\_logs](#module\_policy\_assignment\_logs) + +Source: ../../diagnostic-settings + +Version: +## Outputs + +The following outputs are exported: + +### [policy\_assignment\_id](#output\_policy\_assignment\_id) + +Description: ID of the policy assignment. + +### [policy\_principal\_id](#output\_policy\_principal\_id) + +Description: Principal ID of the system-assigned identity (if created) +## Resources + +The following resources are used by this module: + +- [azurerm_resource_group_policy_assignment.rg_assignment](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/resource_group_policy_assignment) (resource) +- [azurerm_resource_policy_assignment.res_assignment](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/resource_policy_assignment) (resource) +- [azurerm_role_assignment.role](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/role_assignment) (resource) +- [azurerm_subscription_policy_assignment.sub_assignment](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/subscription_policy_assignment) (resource) diff --git a/infrastructure/modules/policy/policy-assignments/variables.tf b/infrastructure/modules/policy/policy-assignments/variables.tf index 703f9eb1..67500aec 100644 --- a/infrastructure/modules/policy/policy-assignments/variables.tf +++ b/infrastructure/modules/policy/policy-assignments/variables.tf @@ -32,6 +32,12 @@ variable "enabled_log" { default = [] } +variable "enabled_metric" { + type = list(string) + description = "Collection of metrics to capture" + default = [] +} + variable "log_analytics_wks_id" { type = string description = "An identifier of a specific Log Analytics Workspace instance to use" @@ -49,7 +55,6 @@ variable "parameters" { description = "Parameters for the policy assignment." } - variable "policy_assignment_scope" { type = string description = "The scope at which this assignment is assigned" @@ -58,6 +63,7 @@ variable "policy_assignment_scope" { variable "policy_assignment_principal_id" { type = string description = "The identifier of a specific service principal to use for the policy assignment" + default = null } variable "policy_identities" { @@ -87,5 +93,3 @@ variable "requires_identity" { description = "True if the policy requires a managed identity, false otherwise" default = false } - - diff --git a/infrastructure/modules/policy/policy-definition/main.tf b/infrastructure/modules/policy/policy-definition/main.tf index 0012490c..4dcbae2a 100644 --- a/infrastructure/modules/policy/policy-definition/main.tf +++ b/infrastructure/modules/policy/policy-definition/main.tf @@ -1,4 +1,4 @@ -resource "azurerm_policy_definition" "item" { +resource "azurerm_policy_definition" "definition" { name = var.name policy_type = coalesce(var.policy_type, "custom") mode = coalesce(var.mode, "all") @@ -10,11 +10,10 @@ resource "azurerm_policy_definition" "item" { "owner" : "security-team@nhs.net" }, var.metadata))) - parameters = try(length(var.parameters) > 0 ? jsonencode(var.parameters) : null, null) + parameters = var.parameters != null ? jsonencode(var.parameters) : null policy_rule = jsonencode(var.policy_rule) } locals { requires_identity = contains(["deployIfNotExists", "modify", "append"], var.policy_rule.then.effect) } - diff --git a/infrastructure/modules/policy/policy-definition/outputs.tf b/infrastructure/modules/policy/policy-definition/outputs.tf index 0482d808..cbf3810f 100644 --- a/infrastructure/modules/policy/policy-definition/outputs.tf +++ b/infrastructure/modules/policy/policy-definition/outputs.tf @@ -1,5 +1,5 @@ output "policy_definition_id" { - value = azurerm_policy_definition.item.id + value = azurerm_policy_definition.definition.id description = "The ID of the created policy definition." } diff --git a/infrastructure/modules/policy/policy-definition/tfdocs.md b/infrastructure/modules/policy/policy-definition/tfdocs.md new file mode 100644 index 00000000..36b028d7 --- /dev/null +++ b/infrastructure/modules/policy/policy-definition/tfdocs.md @@ -0,0 +1,103 @@ +# Module documentation + +## Required Inputs + +The following input variables are required: + +### [display\_name](#input\_display\_name) + +Description: Display name for the policy. + +Type: `string` + +### [name](#input\_name) + +Description: Policy definition name. + +Type: `string` + +### [policy\_rule](#input\_policy\_rule) + +Description: Azure Policy Rule object. Must follow Microsoft schema: +{ + "if": { + | | nested conditions + }, + "then": { + "effect": "deny | audit | modify | denyAction | append | auditIfNotExists | deployIfNotExists | disabled" + "details": + } +} + +Type: + +```hcl +object({ + if = any + then = object({ + effect = string + details = optional(any) + }) + }) +``` + +## Optional Inputs + +The following input variables are optional (have default values): + +### [description](#input\_description) + +Description: Description of the policy. + +Type: `string` + +Default: `""` + +### [metadata](#input\_metadata) + +Description: Metadata for the policy. + +Type: `map(any)` + +Default: `{}` + +### [mode](#input\_mode) + +Description: Determines which resource types are evaluated for a policy definition + +Type: `string` + +Default: `"All"` + +### [parameters](#input\_parameters) + +Description: Policy parameters. + +Type: `map(any)` + +Default: `{}` + +### [policy\_type](#input\_policy\_type) + +Description: Type of policy. + +Type: `string` + +Default: `"Custom"` + +## Outputs + +The following outputs are exported: + +### [policy\_definition\_id](#output\_policy\_definition\_id) + +Description: The ID of the created policy definition. + +### [policy\_requires\_identity](#output\_policy\_requires\_identity) + +Description: True if this policy must be assigned a managed identity, false otherwise +## Resources + +The following resources are used by this module: + +- [azurerm_policy_definition.definition](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/policy_definition) (resource) diff --git a/infrastructure/modules/policy/policy-definition/variables.tf b/infrastructure/modules/policy/policy-definition/variables.tf index 54bbc53d..e3038053 100644 --- a/infrastructure/modules/policy/policy-definition/variables.tf +++ b/infrastructure/modules/policy/policy-definition/variables.tf @@ -1,4 +1,3 @@ - variable "name" { type = string description = "Policy definition name." @@ -52,7 +51,8 @@ variable "policy_rule" { type = object({ if = any then = object({ - effect = string + effect = string + details = optional(any) }) }) validation { @@ -70,6 +70,7 @@ Azure Policy Rule object. Must follow Microsoft schema: }, "then": { "effect": "deny | audit | modify | denyAction | append | auditIfNotExists | deployIfNotExists | disabled" + "details": } } EOT diff --git a/infrastructure/modules/policy/policy-initiatives/tfdocs.md b/infrastructure/modules/policy/policy-initiatives/tfdocs.md new file mode 100644 index 00000000..19718517 --- /dev/null +++ b/infrastructure/modules/policy/policy-initiatives/tfdocs.md @@ -0,0 +1,66 @@ +# Module documentation + +## Required Inputs + +The following input variables are required: + +### [display\_name](#input\_display\_name) + +Description: Initiative display name. + +Type: `string` + +### [name](#input\_name) + +Description: Initiative name. + +Type: `string` + +## Optional Inputs + +The following input variables are optional (have default values): + +### [description](#input\_description) + +Description: Description of the initiative. + +Type: `string` + +Default: `""` + +### [policy\_definitions](#input\_policy\_definitions) + +Description: List of policy definitions included in the initiative. + +Type: + +```hcl +list(object({ + id = string + parameters = optional(any) + reference_id = optional(string) + })) +``` + +Default: `[]` + +### [policy\_type](#input\_policy\_type) + +Description: Type of the initiative, whether it is custom or pre-existing. + +Type: `string` + +Default: `"custom"` + +## Outputs + +The following outputs are exported: + +### [initiative\_id](#output\_initiative\_id) + +Description: ID of the created policy initiative. +## Resources + +The following resources are used by this module: + +- [azurerm_policy_set_definition.item](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/policy_set_definition) (resource) diff --git a/infrastructure/modules/policy/policy-remediation/main.tf b/infrastructure/modules/policy/policy-remediation/main.tf index 139597f9..9580e39f 100644 --- a/infrastructure/modules/policy/policy-remediation/main.tf +++ b/infrastructure/modules/policy/policy-remediation/main.tf @@ -1,2 +1,6 @@ - - +resource "azurerm_resource_policy_remediation" "remediation" { + name = var.remediation_name + policy_assignment_id = var.policy_assignment_id + resource_discovery_mode = "ExistingNonCompliant" + resource_id = var.resource_id +} diff --git a/infrastructure/modules/policy/policy-remediation/outputs.tf b/infrastructure/modules/policy/policy-remediation/outputs.tf index 8b137891..2b674923 100644 --- a/infrastructure/modules/policy/policy-remediation/outputs.tf +++ b/infrastructure/modules/policy/policy-remediation/outputs.tf @@ -1 +1,4 @@ - +output "policy_remediation_id" { + value = azurerm_resource_policy_remediation.remediation.id + description = "The ID of the created policy remediation." +} diff --git a/infrastructure/modules/policy/policy-remediation/tfdocs.md b/infrastructure/modules/policy/policy-remediation/tfdocs.md new file mode 100644 index 00000000..cd29fd73 --- /dev/null +++ b/infrastructure/modules/policy/policy-remediation/tfdocs.md @@ -0,0 +1,42 @@ +# Module documentation + +## Required Inputs + +The following input variables are required: + +### [policy\_assignment\_id](#input\_policy\_assignment\_id) + +Description: The identifier of a specific policy assignment. + +Type: `string` + +### [remediation\_name](#input\_remediation\_name) + +Description: The policy remediation name. + +Type: `string` + +## Optional Inputs + +The following input variables are optional (have default values): + +### [resource\_id](#input\_resource\_id) + +Description: The identifier of a specific resource to apply this policy onto. + +Type: `string` + +Default: `null` + +## Outputs + +The following outputs are exported: + +### [policy\_remediation\_id](#output\_policy\_remediation\_id) + +Description: The ID of the created policy remediation. +## Resources + +The following resources are used by this module: + +- [azurerm_resource_policy_remediation.remediation](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/resource_policy_remediation) (resource) diff --git a/infrastructure/modules/policy/policy-remediation/variables.tf b/infrastructure/modules/policy/policy-remediation/variables.tf index 24043933..5d10815a 100644 --- a/infrastructure/modules/policy/policy-remediation/variables.tf +++ b/infrastructure/modules/policy/policy-remediation/variables.tf @@ -1,10 +1,15 @@ +variable "remediation_name" { + type = string + description = "The policy remediation name." +} -variable "policy_assignment_scope" { +variable "policy_assignment_id" { type = string - description = "The scope at which this assignment is assigned" + description = "The identifier of a specific policy assignment." } -variable "policy_assignment_principal_id" { +variable "resource_id" { type = string - description = "The identifier of a specific service principal to use for the policy assignment" + description = "The identifier of a specific resource to apply this policy onto." + default = null } diff --git a/infrastructure/modules/shared-config/output.tf b/infrastructure/modules/shared-config/output.tf index fb1536d4..9b3cd00a 100644 --- a/infrastructure/modules/shared-config/output.tf +++ b/infrastructure/modules/shared-config/output.tf @@ -93,6 +93,8 @@ locals { network-interface = upper("${var.env}-${var.location_map[var.location]}-${var.application}") network-security-group = upper("NSG-${var.env}-${var.location_map[var.location]}-${var.application}") postgres-sql-server = lower("postgres-${var.application}-${var.env}-${var.location_map[var.location]}") + policy-definition = lower("policy-def-${var.application}-${var.env}-${var.location_map[var.location]}") + policy-assignment = lower("policy-assign-${var.application}-${var.env}-${var.location_map[var.location]}") private-ssh-key = lower("ssh-pri-${var.env}${var.location_map[var.location]}${var.application}") private-link-scope = lower("ampls-${var.env}${var.application}") private-link-scope-private-endpoint = lower("ampls-${var.env}${var.location_map[var.location]}${var.application}-private-endpoint")