Ria C
Ria C

Reputation: 77

Deploying Azure policy ARM template using Azure Devops fails

We have build one Azure policy for "Allowed Locations". Created the required template.json and parameter.json as below: Template.json

While trying to run using Azure pipeline after uploading the json files to Azure repos, below was the error

[error]The request content was invalid and could not be deserialized: 'Required property 'resources' not found in JSON. Path 'properties.template', line 1, position 222.'.

Although resources has been mentioned inside template.json, it fails with this error. Can any one give any insight.

   {
  "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
   "contentVersion": "1.0.0.0",
  "parameters": {
      "listOfAllowedLocations": {
  "type": "array"
    }
  },
  "variables": {},
  "resources": [
   {
  "type": "Microsoft.Authorization/policyDefinitions",
  "name": "policylocation",
  "apiVersion": "2018-03-01",
  "properties": {
    "policyType": "Custom",
    "displayName": "policylocation",
    "description": "",
    "mode": "all",
    "parameters": {
      "listOfAllowedLocations": {
        "type": "array",
        "metadata": {
          "description": "The list of locations that can be specified when deploying resources.",
          "displayName": "Allowed locations"
        }
      }
    },
"policyRule": {
  "if": {
    "allOf": [
      {
        "field": "location",
        "notIn": "EastUS"
      },
      {
        "field": "location",
        "notEquals": "global"
      },
      {
        "field": "type",
        "notEquals": "Microsoft.Compute/virtualMachines"
      }
    ]
  },
  "then": {
    "effect": "deny"
  }
}
  }
}
  ]
}

Parameter.json

   {
 "$schema": "https://schema.management.azure.com/schemas/2015-01- 
  01/deploymentParameters.json#",
 "contentVersion": "1.0.0.0",
 "parameters": {
"listOfAllowedLocations": {
  "type":"array",
  "value": "EastUS"
   }
   }
 }

Upvotes: 1

Views: 2101

Answers (2)

Rob S.
Rob S.

Reputation: 1146

When I attempt deploying your policy with the given template and parameter file, I receive the following error.

{
    "error": {
        "code": "InvalidDeploymentParameterType",
        "message": "The type of deployment parameter 'listOfAllowedLocations' should not be specified. Please see https://aka.ms/resource-manager-parameter-files for details."
    }
}

This means you have a parameter (listOfAllowedLocations) which is unused. While this may be okay for most language schemas to have an unused parameter, for policy it is not. Start by removing this parameter or adding this parameter to your policy so that it is used.

Next, based on the misleading error message you are receiving, I am curious about your method of deployment. Policies can be deployed many different ways. Portal, Powershell, REST API, just to name a few. I prefer the REST API method because it offers quite a bit of flexibility and simplicity in definition and use. If you chose the REST API there are actually two different methods you could choose (as an Azure Deployment or as a Policy Definition) respectively those are the following endpoints.

PUT https://management.azure.com/subscriptions/{subscriptionId}/providers/Microsoft.Resources/deployments/{deploymentName}?api-version=2019-10-01

DOCS - https://learn.microsoft.com/en-us/rest/api/resources/deployments/createorupdate

PUT https://management.azure.com/subscriptions/{subscriptionId}/providers/Microsoft.Authorization/policyDefinitions/{policyDefinitionName}?api-version=2019-09-01

DOCS - https://learn.microsoft.com/en-us/rest/api/resources/policydefinitions/createorupdate

My preference is the deployment route, just because it uses the azure deployment mechanism to deploy the policy which offers a consistent and user friendly method of troubleshooting, retrying, and inspecting. It also allows you to deploy policies as template files and parameter files, nest deployments within deployments (which can be useful in more complex use cases), and specify parameters at a deployment scope as well as at the policy scope. However, there are also some limitations to deployments, for instance the per subscription and resource group quota (currently 800). Some periodic house cleaning will help with this.

Using the Azure Deployment REST API methodology I would encourage you to attempt one of the following depending on what your intentions are.

Option 1a: You want to keep the 'listOfAllowedLocations' as a parameter and use it in your policy. You also want to apply the parameter at the DEPLOYMENT scope so that the resulting deployed policy has a statically defined list of allowed locations.

PUT https://management.azure.com/subscriptions/{subscriptionId}/resourcegroups/{resourceGroupName}/providers/Microsoft.Resources/deployments/{deploymentName}?api-version=2019-10-01

BODY:

{
    "location": "eastus",
    "properties": {
        "mode": "Incremental",
        "parameters": {
            "listOfAllowedLocations": {
                "value": ["eastus"]
            }
        },
        "template": {
            "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
            "contentVersion": "1.0.0.0",
            "parameters": {
                "listOfAllowedLocations": {
                    "type": "array"
                }
            },
            "variables": {},
            "resources": [
                {
                    "type": "Microsoft.Authorization/policyDefinitions",
                    "name": "policylocation",
                    "apiVersion": "2018-03-01",
                    "properties": {
                        "policyType": "Custom",
                        "displayName": "policylocation",
                        "description": "",
                        "mode": "all",
                        "parameters": {},
                        "policyRule": {
                            "if": {
                                "allOf": [
                                    {
                                        "field": "location",
                                        "notIn": "[parameters('listOfAllowedLocations')]"
                                    },
                                    {
                                        "field": "location",
                                        "notEquals": "global"
                                    },
                                    {
                                        "field": "type",
                                        "notEquals": "Microsoft.Compute/virtualMachines"
                                    }
                                ]
                            },
                            "then": {
                                "effect": "deny"
                            }
                        }
                    }
                }
            ]
        }
    }
}

Option 1b: You want to keep the 'listOfAllowedLocations' as a parameter and use it in your policy. You also want to apply the parameter at the POLICY DEFINITION scope so that the resulting deployed list of allowed locations can be manipulated upon assignment. Notice the subtle difference in the scoping of the parameter and the escaping of the of the parameter within the policy resource definition ('[[').

PUT https://management.azure.com/subscriptions/{subscriptionId}/resourcegroups/{resourceGroupName}/providers/Microsoft.Resources/deployments/{deploymentName}?api-version=2019-10-01

BODY:

{
    "location": "eastus",
    "properties": {
        "mode": "Incremental",
        "parameters": {},
        "template": {
            "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
            "contentVersion": "1.0.0.0",
            "parameters": {},
            "variables": {},
            "resources": [
                {
                    "type": "Microsoft.Authorization/policyDefinitions",
                    "name": "policylocation",
                    "apiVersion": "2018-03-01",
                    "properties": {
                        "policyType": "Custom",
                        "displayName": "policylocation",
                        "description": "",
                        "mode": "all",
                        "parameters": {
                            "listOfAllowedLocations": {
                                "type": "array",
                                "defaultValue": ["eastus"]
                            }
                        },
                        "policyRule": {
                            "if": {
                                "allOf": [
                                    {
                                        "field": "location",
                                        "notIn": "[[parameters('listOfAllowedLocations')]"
                                    },
                                    {
                                        "field": "location",
                                        "notEquals": "global"
                                    },
                                    {
                                        "field": "type",
                                        "notEquals": "Microsoft.Compute/virtualMachines"
                                    }
                                ]
                            },
                            "then": {
                                "effect": "deny"
                            }
                        }
                    }
                }
            ]
        }
    }
}

Option 2: Static definition of the allowed location. This will basically circumvent the process of passing the parameter through either the deployment or the policy assignment.

{
    "location": "eastus",
    "properties": {
        "mode": "Incremental",
        "parameters": {},
        "template": {
            "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
            "contentVersion": "1.0.0.0",
            "parameters": {},
            "variables": {},
            "resources": [
                {
                    "type": "Microsoft.Authorization/policyDefinitions",
                    "name": "policylocation",
                    "apiVersion": "2018-03-01",
                    "properties": {
                        "policyType": "Custom",
                        "displayName": "policylocation",
                        "description": "",
                        "mode": "all",
                        "parameters": {},
                        "policyRule": {
                            "if": {
                                "allOf": [
                                    {
                                        "field": "location",
                                        "notIn": ["eastus"]
                                    },
                                    {
                                        "field": "location",
                                        "notEquals": "global"
                                    },
                                    {
                                        "field": "type",
                                        "notEquals": "Microsoft.Compute/virtualMachines"
                                    }
                                ]
                            },
                            "then": {
                                "effect": "deny"
                            }
                        }
                    }
                }
            ]
        }
    }
}

Upvotes: 1

Kemley
Kemley

Reputation: 204

The problem is that the policy is not leverage the listOfAllowedLocations parameters. I would remove it and make the parameter just empty brakets.

Here is some reasource: https://review.learn.microsoft.com/en-us/azure/governance/policy/concepts/definition-structure?branch=pr-en-us-116104

Upvotes: 0

Related Questions