Skip to content

feat: Add proper validation for non-compliance messages in policy assignments#243

Merged
ASHR4 merged 3 commits intomainfrom
feature/ncm-validation
Apr 7, 2026
Merged

feat: Add proper validation for non-compliance messages in policy assignments#243
ASHR4 merged 3 commits intomainfrom
feature/ncm-validation

Conversation

@ASHR4
Copy link
Copy Markdown
Contributor

@ASHR4 ASHR4 commented Mar 27, 2026

Adding validation logic to ensure that a policy assignment contains at most one default non-compliance message (a message without a PolicyDefinitionReferenceID).

Supports #201

@codecov-commenter
Copy link
Copy Markdown

codecov-commenter commented Mar 27, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 47.79%. Comparing base (3181cd0) to head (a9f5a23).

Additional details and impacted files
@@            Coverage Diff             @@
##             main     #243      +/-   ##
==========================================
+ Coverage   47.57%   47.79%   +0.21%     
==========================================
  Files          54       54              
  Lines        4847     4865      +18     
==========================================
+ Hits         2306     2325      +19     
  Misses       2254     2254              
+ Partials      287      286       -1     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@ASHR4 ASHR4 marked this pull request as ready for review March 27, 2026 14:32
@ASHR4 ASHR4 enabled auto-merge March 27, 2026 15:21
Comment thread assets/policyAssignment.go
Co-authored-by: Matt White <16320656+matt-FFFFFF@users.noreply.github.com>
@matt-FFFFFF
Copy link
Copy Markdown
Member

Thanks @ASHR4 just getting a review form copilot then will merge

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds validation to assets.PolicyAssignment to ensure there is at most one default non-compliance message (i.e., a non-compliance message without a PolicyDefinitionReferenceID), aligning policy assignment validation with Azure constraints.

Changes:

  • Add validation in ValidatePolicyAssignment to detect multiple default non-compliance messages.
  • Add unit tests covering single vs. multiple default non-compliance messages (including empty reference IDs).

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.

File Description
assets/policyAssignment.go Introduces validation logic counting “default” non-compliance messages and errors when more than one is present.
assets/policyAssignment_test.go Adds table-driven test cases verifying the new validation behavior.

Comment thread assets/policyAssignment.go Outdated
Comment thread assets/policyAssignment.go
Comment thread assets/policyAssignment_test.go
@matt-FFFFFF
Copy link
Copy Markdown
Member

@ASHR4 some decent comments here, worth a look

@ASHR4
Copy link
Copy Markdown
Contributor Author

ASHR4 commented Mar 30, 2026

@matt-FFFFFF I've implemented the copilot recommendations 😄

@ASHR4 ASHR4 requested a review from matt-FFFFFF March 30, 2026 08:30
@ASHR4 ASHR4 added this pull request to the merge queue Apr 7, 2026
Merged via the queue into main with commit 8eb327c Apr 7, 2026
8 checks passed
@ASHR4 ASHR4 deleted the feature/ncm-validation branch April 7, 2026 10:06
@thisispr
Copy link
Copy Markdown

how do you handle this ? @matt-FFFFFF @ASHR4

~ module.lz_vending.module.lz_vending["xx"].data.azapi_resource_list.subscription_management_group_association[0] will be read during apply
# (depends on a resource or a module with changes pending)
~ module.lz_vending.module.lz_vending["xx"].data.azapi_resource_list.subscriptions[0] will be read during apply
# (depends on a resource or a module with changes pending)
+ module.lz_vending.module.lz_vending["xx"].azapi_resource_action.subscription_association[0] will be created
! module.lz_vending.module.lz_vending["xx"].terraform_data.replacement[0] will be updated in-place
+ module.management_groups.module.management_groups.azapi_resource.policy_assignments["xx-landingzones/Deny-AKS-AllowedImages"] will be created
# module.management_groups.module.management_groups.azapi_resource.policy_assignments["xx-landingzones/Deny-AKS-AllowedImages"] will be created
  + resource "azapi_resource" "policy_assignments" {
      + body                             = {
          + properties = {
              + definitionVersion     = "9.*.*"
              + description           = "Deny AKS clusters from using container images not from allowed registries."
              + displayName           = "xx - AKS allowed container registries"
              + enforcementMode       = "DoNotEnforce"
              + metadata              = {
                  + createdBy = ""
                  + createdOn = ""
                  + updatedBy = ""
                  + updatedOn = ""
                }
              + nonComplianceMessages = [
                  + {
                      + message = "This resource should be compliant with the assigned policy."
                    },
                ]
              + notScopes             = []
              + overrides             = []
              + parameters            = {
                  + allowedContainerImagesRegex = {
                      + value = "^(acrdevxxszn\\.azurecr\\.io|acrxx\\.azurecr\\.io|mcr\\.microsoft\\.com|ghcr\\.io|quay\\.io|ecr-public\\.aws\\.com|registry-1\\.docker\\.io|docker\\.io|temporalio|apache|changemakerstudiosus|weblate|dpage|bitnamilegacy)/.+$"
                    }
                  + effect                      = {
                      + value = "deny"
                    }
                  + excludedNamespaces          = {
                      + value = [
                          + "kube-system",
                          + "gatekeeper-system",
                          + "azure-arc",
                          + "azure-extensions-usage-system",
                        ]
                    }
                }
              + policyDefinitionId    = "/providers/Microsoft.Authorization/policyDefinitions/febd0533-8e55-448f-b837-bd0e06f16469"
              + resourceSelectors     = []
            }
        }
      + id                               = (known after apply)
      + ignore_casing                    = false
      + ignore_missing_property          = true
      + ignore_null_property             = false
      + location                         = "switzerlandnorth"
      + name                             = "Deny-AKS-AllowedImages"
      + output                           = (known after apply)
      + parent_id                        = "/providers/Microsoft.Management/managementGroups/xx-landingzones"
      + replace_triggers_external_values = [
          + "/providers/Microsoft.Authorization/policyDefinitions/febd0533-8e55-448f-b837-bd0e06f16469",
          + "switzerlandnorth",
        ]
      + response_export_values           = []
      + retry                            = {
          + error_message_regex  = [
              + "AuthorizationFailed",
              + "The policy definition specified in policy assignment '.+' is out of scope",
            ]
          + interval_seconds     = 5
          + max_interval_seconds = 30
          + multiplier           = 1.5
          + randomization_factor = 0.5
        }
      + schema_validation_enabled        = true
      + sensitive_body                   = (write-only attribute)
      + type                             = "Microsoft.Authorization/policyAssignments@2024-04-01"

      + identity {
          + identity_ids = []
          + principal_id = (known after apply)
          + tenant_id    = (known after apply)
          + type         = "None"
        }

      + timeouts {
          + create = "60m"
          + delete = "5m"
          + read   = "60m"
          + update = "5m"
        }
    }

Apply error:

module.management_groups.module.management_groups.azapi_resource.policy_assignments["xx-landingzones/Deny-AKS-AllowedImages"]: Creating...

Error: Failed to create/update resource

  with module.management_groups.module.management_groups.azapi_resource.policy_assignments["xx-landingzones/Deny-AKS-AllowedImages"],
  on .terraform/modules/management_groups.management_groups/main.policy_assignments.tf line 5, in resource "azapi_resource" "policy_assignments":
   5: resource "azapi_resource" "policy_assignments" {

creating/updating Resource: (ResourceId
"/providers/Microsoft.Management/managementGroups/xx-landingzones/providers/Microsoft.Authorization/policyAssignments/Deny-AKS-AllowedImages"
/ Api Version "2024-04-01"): PUT
https://management.azure.com/providers/Microsoft.Management/managementGroups/xx-landingzones/providers/Microsoft.Authorization/policyAssignments/Deny-AKS-AllowedImages
--------------------------------------------------------------------------------
RESPONSE 400: 400 Bad Request
ERROR CODE: InvalidCreatePolicyAssignmentRequest
--------------------------------------------------------------------------------
{
  "error": {
    "code": "InvalidCreatePolicyAssignmentRequest",
    "message": "The policy assignment request is invalid. A non-compliance message is being created for a policy definition with mode 'Microsoft.Kubernetes.Data'. Non-compliance messages are not supported for this mode."
  }
}

@ASHR4
Copy link
Copy Markdown
Contributor Author

ASHR4 commented Apr 16, 2026

Hi @thisispr, the validation that was added as part of this PR, validates if multiple default non-compliance messages are provided to a single assignment which is not allowed.

Your error looks related to the fact you're assigning a policy that has a resource provider mode, Microsoft.Kubernetes.Data, which does not support non-compliance messages.

image

https://learn.microsoft.com/en-us/azure/governance/policy/concepts/definition-structure-basics#resource-manager-modes

https://learn.microsoft.com/en-us/azure/governance/policy/concepts/assignment-structure#metadata

This can be handled via this variable in the avm-ptn-alz module, where you can exclude unsupported assignments.

For example:

policy_assignment_non_compliance_message_settings = {
  fallback_message_unsupported_assignments = [
        "Deny-Privileged-AKS",
        "Enforce-AKS-HTTPS",
        "Deny-Priv-Esc-AKS",
        "Deny-AKS-AllowedImages" +++
      ]
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants