0

I'm deploying Subscription-level cost analysis components into an Azure Dashboard and need assistance on a "deserialization" error. Each Subscription requires two cost perspectives displayed in the dashboard. I use a Bicep for loop to step through the subscription information provided, using the parameter index to calculate the position for the next tile within the dashboard.

I start off with a deploy YAML file, calling a Bicep module and providing a JSON parameter file. This parameter file contains an array of objects, which I pass through to the Bicep module.

I receive the following error:

Unable to process the preflight request.
Exception message: Cannot deserialize the current JSON array (e.g. [1,2,3]) into type 
'Microsoft.WindowsAzure.ResourceStack.Providers.Feature.Definitions.DashboardPartDefinition' 
because the type requires a JSON object (e.g. {"name":"value"}) to deserialize correctly.

JSON parameter file:

{
    "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
    "contentVersion": "1.0.0.0",
    "parameters": {
        "landingZoneSubscriptions": {
            "value": [
                {
                    "name": "sub1Name",
                    "id": "sub1Id"
                },
                {
                    "name": "sub1Name",
                    "id": "sub1Id"
                },
                {
                    "name": "sub1Name",
                    "id": "sub1Id"
                },
                {
                    "name": "sub1Name",
                    "id": "sub1Id"
                }                             
            ]
        }
    }
} 

YAML extract:

inputs:
   ...
   templateLocation: 'Linked artifact'
   csmFile: $(Build.Repository.LocalPath)/infra-alz/templates/pdb-standardPlatform.bicep
   csmParametersFile: $(Build.Repository.LocalPath)/pdb-standardPlatform.json
   ...

Bicep extract:

...
@description('An array of subscription objects, each containing "name" and "id".')
param landingZoneSubscriptions array
...
...
var landingZoneCosts = [for i in range(0, length(landingZoneSubscriptions)) : {
    position: {
      x: 0 + ((i % 4) * 6)
      y: 4 + ((i / 4) * 4)
      colSpan: 6
      rowSpan: 4
    }
    metadata: {
      inputs: [
        {
          name: 'scope'
          value: '/subscriptions/${landingZoneSubscriptions[i].id}'
        }
        {
          name: 'scopeName'
          value: '/subscriptions/${landingZoneSubscriptions[i].name}'
        }
        {
          name: 'view'
          value: {
            displayName: 'Daily Costs by WorkloadName'
            currency: costCurrency
            dateRange: 'Last3Months'
            query: {
              type: 'ActualCost'
              dataSet: {
                granularity: 'Daily'
                aggregation: {
                  totalCost: {
                    name: 'Cost'
                    function: 'Sum'
                  }
                  totalCostUSD: {
                    name: 'CostUSD'
                    function: 'Sum'
                  }
                }
                sorting: [
                  {
                    name: 'BillingMonth'
                    direction: 'ascending'
                  }
                ]
                grouping: [
                  {
                    type: 'TagKey'
                    name: 'WorkloadName'
                  }
                ]
              }
              timeframe: 'None'
            }
            chart: 'StackedColumn'
            accumulated: false
          }
        }
      ]
      type: 'Extension/Microsoft_Azure_CostManagement/PartType/CostAnalysisPinPart'
    }
  }]

Error:

##[error]PreflightProcessingError: Unable to process the preflight request.
Exception message: Cannot deserialize the current JSON array (e.g. [1,2,3]) into type 
'Microsoft.WindowsAzure.ResourceStack.Providers.Feature.Definitions.DashboardPartDefinition' 
because the type requires a JSON object (e.g. {"name":"value"}) to deserialize correctly.

I've confirmed the JSON structure provides and array of objects (each containing a name and an id).

I've tried (unsuccessfully) to pass the parameters as other types, but the dashboard component requires an object...which is, as far as I can determine, being passed as an object.

Any assistance greatly appreciated!

1
  • Convert the landingZoneCosts from an array to an object with unique keys in your Bicep loop, since the part's property expects a JSON object, not an array. Reference: Azure Dashboard Structure – DashboardParts Commented May 19 at 7:46

1 Answer 1

0

Deploying Azure dashboard tiles using JSON array of objects parameter

The error description you shared is generally occurring when you're not able to differentiate between what Azure Dashboards expect in a parts property versus what you're generating in your Bicep loop.

In the configuration you shared, you're passing an array of parts, but what Azure expects is an object where each key is a unique part name.

Make changes in the JSON as mentioned below

JSON:

"parts": {
  "costTile1": { ... },
  "costTile2": { ... }
}

Here, you're converting your landingZoneCosts array into an object with named keys.

As you make the changes in the JSON, make the changes in the configuration part as well.

var landingZoneCosts = {
  for i in range(0, length(landingZoneSubscriptions)): 
  'costTile${i}': {
    position: {
      x: 0 + ((i % 4) * 6)
      y: 4 + ((i / 4) * 4)
      colSpan: 6
      rowSpan: 4
    }
    metadata: {
      inputs: [
        {
          name: 'scope'
          value: '/subscriptions/${landingZoneSubscriptions[i].id}'
        }
        {
          name: 'scopeName'
          value: landingZoneSubscriptions[i].name
        }
        {
          name: 'view'
          value: {
            displayName: 'Daily Costs by WorkloadName'
            currency: costCurrency
            dateRange: 'Last3Months'
            query: {
              type: 'ActualCost'
              dataSet: {
                granularity: 'Daily'
                aggregation: {
                  totalCost: {
                    name: 'Cost'
                    function: 'Sum'
                  }
                  totalCostUSD: {
                    name: 'CostUSD'
                    function: 'Sum'
                  }
                }
                sorting: [
                  {
                    name: 'BillingMonth'
                    direction: 'ascending'
                  }
                ]
                grouping: [
                  {
                    type: 'TagKey'
                    name: 'WorkloadName'
                  }
                ]
              }
              timeframe: 'None'
            }
            chart: 'StackedColumn'
            accumulated: false
          }
        }
      ]
      type: 'Extension/Microsoft_Azure_CostManagement/PartType/CostAnalysisPinPart'
    }
  }
}

Once these changes are made, then you can continue with the dashboard resource definition under the properties.parts.

Refer:

https://learn.microsoft.com/en-us/azure/azure-portal/azure-portal-dashboards-structure#dashboardparts

Sign up to request clarification or add additional context in comments.

2 Comments

Thanks @Vinay B. However, the "metadata - inputs" section requires a string identifier ("i" in this case) but we've provided an int, leading to error "Indexing over objects requires an index of type "string" but the provided index was of type "int". So, for example, this line "value: '/subscriptions/${landingZoneSubscriptions[i].value.id}'" errors on the i. Any ideas?
Use a var to convert the array to an object first with string keys, then loop over that object—e.g.,var subMap = { for (i, sub) in landingZoneSubscriptions: string(i): sub } Then index using subMap['${i}'] to avoid the int-to-string issue @ChrisP

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.