Azure / terraform-provider-alz

Azure Landing Zones Terraform provider
https://registry.terraform.io/providers/Azure/alz/latest
MIT License
17 stars 7 forks source link

bug: conversion of resource and override selectors includes empty arrays rather than nil values #150

Open matt-FFFFFF opened 2 hours ago

matt-FFFFFF commented 2 hours ago

Source Azure/terraform-azurerm-avm-ptn-alz#132#issuecomment-2439924137

Thanks to @JWilkinsonMB

repro code:

Audit-ResourceRGLocation = {
          resource_selectors = [{
            name = "ResourceTypeExclusions"
            resource_selector_selectors = [{
              kind = "resourceType"
              not_in = [
                "microsoft.network/networkwatchers",
                "microsoft.network/virtualhubs"
              ]
            }]
          }]
        }

Apply output:

# module.alz.module.alz_global.azapi_resource.policy_assignments["global/Audit-ResourceRGLocation"] will be updated in-place
  ~ resource "azapi_resource" "policy_assignments" {
      ~ body                             = {
          ~ properties = {
              ~ resourceSelectors     = null -> [
                  + {
                      + name      = "ResourceTypeExclusions"
                      + selectors = [
                          + {
                              + in    = []
                              + kind  = "resourceType"
                              + notIn = [
                                  + "microsoft.network/networkwatchers",
                                  + "microsoft.network/virtualhubs",
                                ]
                            },
                        ]
                    },
                ]
                # (9 unchanged attributes hidden)
            }
        }
    }
│ Error: Failed to create/update resource
│
│   with module.alz.module.alz_global.azapi_resource.policy_assignments["global/Audit-ResourceRGLocation"],
│   on .terraform\modules\alz.alz_global\main.policy_assignments.tf line 1, in resource "azapi_resource" "policy_assignments":
│    1: resource "azapi_resource" "policy_assignments" {
│
│ creating/updating Resource: (ResourceId "/providers/Microsoft.Management/managementGroups/global/providers/Microsoft.Authorization/policyAssignments/Audit-ResourceRGLocation" /   
│ Api Version "2024-04-01"): PUT
│ https://management.azure.com/providers/Microsoft.Management/managementGroups/global/providers/Microsoft.Authorization/policyAssignments/Audit-ResourceRGLocation
│ --------------------------------------------------------------------------------
│ RESPONSE 400: 400 Bad Request
│ ERROR CODE: InvalidRequestContent
│ --------------------------------------------------------------------------------
│ {
│   "error": {
│     "code": "InvalidRequestContent",
│     "message": "The request content was invalid and could not be deserialized: 'Invalid operator. Please use only one of the allowed operators: 'in', 'notIn'.'."
│   }
│ }
│ --------------------------------------------------------------------------------
│

Suspect code:

https://github.com/Azure/terraform-provider-alz/blob/8203c5dd1fea8e754e69b6ebc75769e4c84e4d31/internal/provider/architecture_data_source.go#L333-L363

Proposed solution is to initialize a nil variable first then perform a length check:

// Convert NotIn to a go slice, start off from an uninitialized slice so that the value is nil if the input is empty.
var notIn []*string
if len(rssv.NotIn.Elements()) != 0 {
    var err error
    notIn, err = frameworktype.SliceOfPrimitiveToGo[string](ctx, rssv.NotIn.Elements())
    if err != nil {
        resp.Diagnostics.AddError(
            "convertPolicyAssignmentResourceSelectorsToSdkType: error",
            fmt.Sprintf("unable to convert ResourceSelectorSelectorsValue.NotIn elements to Go slice: %s", err.Error()),
        )
        return nil
    }
}
matt-FFFFFF commented 2 hours ago

Have implemented the above in #151 and updated existing test to ensure this works