skip to Main Content

I’m currently trying to deploy a policy initiative definition to Azure using Terraform. Everything works fine except for policies where I use parameter values that are Integer. To pin down the problem I created a small example to test the deployment:

main.tf

resource "azurerm_policy_set_definition" "instance" {
  name         = "INT TEST"
  display_name = "INT TEST"

  policy_type = "Custom"

  management_group_id = "/providers/microsoft.management/managementgroups/example_management_group"

  parameters = jsonencode(var.initiative_params_int)

  dynamic "policy_definition_reference" {
    for_each = var.initiative_with_int_parameters
    iterator = policy_definition
    content {
      policy_definition_id = policy_definition.key
      parameter_values     = length(policy_definition.value) == 0 ? null : jsonencode({ for name, value in tomap(policy_definition.value) : name => { "value" = value } })
    }
  }
}

variables.tf

variable "initiative_with_int_parameters" {
  default = {
    "/providers/Microsoft.Authorization/policyDefinitions/cee51871-e572-4576-855c-047c820360f0" = {
      effect            = "[parameters('denyAll')]"
      minimumRSAKeySize = 2048
    }
    "/providers/Microsoft.Authorization/policyDefinitions/82067dbb-e53b-4e06-b631-546d197452d9" = {
      effect            = "[parameters('denyAll')]"
      minimumRSAKeySize = 2048
    }
    "/providers/Microsoft.Authorization/policyDefinitions/12430be1-6cc8-4527-a9a8-e3d38f250096" = {
      effect          = "[parameters('denyAll')]"
      modeRequirement = "Prevention"
    }
  }
}

variable "initiative_params_int" {
  default = {
    denyAll = {
      type         = "String"
      defaultValue = "Deny"
      metadata = {
        displayName = "Deny All"
        description = "Deny all requests"
      }
    }
  }
}

When I try to run "terraform apply" and enter "yes" I get the following error:

Error: creating Policy Set Definition "INT TEST": policy.SetDefinitionsClient#CreateOrUpdateAtManagementGroup: Failure responding to request: StatusCode=400 -- Original Error: autorest/azure: Service returned an error. Status=400 Code="InvalidPolicyParameterType" Message="The policy parameter 'minimumRSAKeySize' does not match the expected parameter type defined in the policy definition 'cee51871-e572-4576-855c-047c820360f0'. Details 'The expected policy parameter type: 'Integer'. The actual policy parameter type 'String'.'."
│
│   with azurerm_policy_set_definition.instance,
│   on main.tf line 137, in resource "azurerm_policy_set_definition" "instance":
│  137: resource "azurerm_policy_set_definition" "instance" {

When I leave out the above two policies and only use the third one everything works and the deployment is successful.

I thought that an explicit typecast might help minimumRSAKeySize = tonumber(2048) but apparently I can’t do a typecast to number on an Integer value. I also tried to input the values for "minimumRSAKeySize" as String but then I get the same error mentioned above.

2

Answers


  1. Chosen as BEST ANSWER

    I found a solution to the problem. During the assignment of the attribute "parameter_values" inside the policy_definition_reference block the values of the parameters are converted to string:

    parameter_values     = length(policy_definition.value) == 0 ? null : jsonencode({ for name, value in tomap(policy_definition.value) : name => { "value" = value } })
    

    I adjusted the file variables.tf as follows:

    variable "initiative_with_int_parameters_transformed" {
      default = {
        "/providers/Microsoft.Authorization/policyDefinitions/cee51871-e572-4576-855c-047c820360f0" = {
          effect            = {
            value = "[parameters('denyAll')]"
          }
          minimumRSAKeySize = {
            value = 2048
          }
        }
        "/providers/Microsoft.Authorization/policyDefinitions/82067dbb-e53b-4e06-b631-546d197452d9" = {
          effect            = {
            value = "[parameters('denyAll')]"
          }
          minimumRSAKeySize = {
            value = 2048
          }
        }
        "/providers/Microsoft.Authorization/policyDefinitions/12430be1-6cc8-4527-a9a8-e3d38f250096" = {
          effect            = {
            value = "[parameters('denyAll')]"
          }
          modeRequirement = {
            value = "Prevention"
          }
        }
      }
    }
    

    Then the assignment of the policy parameters can be done like this:

    parameter_values     = length(policy_definition.value) == 0 ? null : jsonencode(policy_definition.value)
    

    This preserves the parameter types and doesn't throw any errors when deploying the initiative using terraform apply.


  2. I suspect the problem here is that you haven’t defined a type constraint for your input variable and so Terraform is attempting to guess what type constraint you intended, but is guessing incorrectly and concluding that you intended to declare type = map(map(string)), which therefore forces converting minimumRSAKeySize to a string.

    If that is the problem then you should be able to fix it by declaring an explicit type constraint on each of your input variables so that Terraform does not need to guess. For example:

    variable "initiative_with_int_parameters" {
      type = map(object({
        effect            = string
        minimumRSAKeySize = optional(number)
        modeRequirement   = optional(string)
      }))
    
      default = {
        "/providers/Microsoft.Authorization/policyDefinitions/cee51871-e572-4576-855c-047c820360f0" = {
          effect            = "[parameters('denyAll')]"
          minimumRSAKeySize = 2048
        }
        "/providers/Microsoft.Authorization/policyDefinitions/82067dbb-e53b-4e06-b631-546d197452d9" = {
          effect            = "[parameters('denyAll')]"
          minimumRSAKeySize = 2048
        }
        "/providers/Microsoft.Authorization/policyDefinitions/12430be1-6cc8-4527-a9a8-e3d38f250096" = {
          effect          = "[parameters('denyAll')]"
          modeRequirement = "Prevention"
        }
      }
    }
    
    variable "initiative_params_int" {
      type = map(object({
        type         = string
        defaultValue = string
        metadata = object({
          displayName = string
          description = string
        })
      }))
    
      default = {
        denyAll = {
          type         = "String"
          defaultValue = "Deny"
          metadata = {
            displayName = "Deny All"
            description = "Deny all requests"
          }
        }
      }
    }
    

    In general, all Terraform variables ought to have a type constraint. Terraform does not require it for backward compatibility with very old versions of Terraform that had a much more limited type system, but telling Terraform exactly the type you intend means that your configuration will be more readable, Terraform won’t need to guess what type constraint you meant, and Terraform will be able to give you better feedback if you use an incorrect value to define this variable.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search