1

Later edit:

I maintain a global template file azuredeploy.parameters.json.template with placeholders. Here are some of the params, including the one - ipFilter - I'm having issues with. If I don't use this parameter, it passes the validation and the deployment works correctly:

{
  "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentParameters.json#",
  "contentVersion": "1.0.0.0",
  "parameters": {
    "resourceGroupName": {
      "value": "rg-mine-<%= $environment %>"
    },
    "location": {
      "value": "westeurope"
    },
    "environment": {
      "value": "<%= $environment %>"
    },
    "ipFilter": {
        "value": <%= (ConvertTo-Json $ipFilter) %>    
    },
    "buildSuffix": {
      "value": {
        "name": "",
        "number": ""
      }
    },
    "serverFarm": {
      "value": {
        "name": "asp-landscape-<%= $environment %>",
        "resourceGroup": "rg-landscape-<%= $environment %>",
        "resourceType": "Microsoft.Web/serverFarms"
      }
    }
    ....

    other params
  }
}

A PS script (Build-TemplateParameterFiles) reads the global template, merges in the env file as a hashtable, renders via Invoke‑EpsTemplate, and writes out the final azuredeploy.parameters.<env>.json:

function Build-TemplateParameterFiles {
    [CmdletBinding()]
    Param(
        [Parameter(Mandatory = $true)]
        [string]$Location,
        [Parameter(Mandatory = $true)]
        [object[]]$Environments
    )

    $templateFileName = 'azuredeploy.parameters.json.template'
    $templateFileText = [System.IO.File]::ReadAllText($Location + $templateFileName)

    Write-Host "Start building parameter files ..."

    foreach ($environment in $Environments) {
        $parameterFileName = 'azuredeploy.parameters.' + $environment + '.json'

        Write-Host "Building $parameterFileName ..."

        $parameterObject = Get-Content $($Location + $parameterFileName) -Raw `
        | ConvertFrom-Json -AsHashtable

        $parameterObject += @{ environment = $environment }

        Invoke-EpsTemplate -Template $templateFileText -Binding $parameterObject -Safe `
        | Set-Content $($Location + $parameterFileName) 

        Write-Host "$parameterFileName Build!"
    }

    Write-Host "Done building parameter files!"
}

Export-ModuleMember -Function Build-TemplateParameterFiles

For each environment (e.g., dvlp), the powershell script generates I have another JSON file that contains just the environment‑specific values. For dvlp, I'll have azuredeploy.parameters.dvlp.json. I've added a step in the pipeline to print out the file after the step that builds the template file, and right before moving to the validation template step. Here's what it looks like:

{
  "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentParameters.json#",
  "contentVersion": "1.0.0.0",
  "parameters": {
    "resourceGroupName": {
      "value": "rg-mine-dvlp"
    },
    "location": {
      "value": "westeurope"
    },
    "ipFilter": {
      "value": ["123", "123"]
    },
    "environment": {
      "value": "dvlp"
    },
    "buildSuffix": {
      "value": {
        "name": "",
        "number": ""
      }
    },
    "serverFarm": {
      "value": {
        "name": "asp-landscape-dvlp",
        "resourceGroup": "rg-landscape-dvlp",
        "resourceType": "Microsoft.Web/serverFarms"
      }
    }
  }
}

Then, this gets validated in the pipeline by the default task Azure DevOps provides when designing/editing a new pipeline. Here's the step:

  - task: AzureResourceManagerTemplateDeployment@3
    displayName: "Validate ARM Templates"
    inputs:
      deploymentScope: "Subscription"
      azureResourceManagerConnection: "$(azureServiceConnectionDvlp)"
      subscriptionId: "$(subscriptionIdDvlp)"
      location: $(DEFAULT_LOCATION)
      csmFile: $(rootDirectory)/templates/azuredeploy.json
      csmParametersFile: $(rootDirectory)/templates/azuredeploy.parameters.dvlp.json
      deploymentMode: Validation
      overrideParameters: >
        -buildSuffix { "name": "$(Build.DefinitionName)", "number":  "$(Build.BuildNumber)" }

And here's where the problem arises and sticks. I always get the following exception, no matter the combination of raw vs escaped tags, quoting, indexing ($ipFilter['allowedIps'] vs $ipFilter.allowedIps vs $ipFilter.value):

##[error]Error: Ensure the Parameters file ('…azuredeploy.parameters.dvlp.json') is valid. Task failed while parsing with the following error: Expected double-quoted property name in JSON at position 2312

I also thought that perhaps ipFilter is a reserved keyword and replace it with just abc, but the exception persists.

What am I missing here? One challenge: I cannot change the pipeline nor the PS script that builds the params file, as these are consistent across the entire project/all repos.

3
  • would love to help you, but as it stands someone would need a reproducible example in order to support this. can you edit the question to include more details or produce a gist that recreates the error? Commented Jul 28 at 19:19
  • @bryanbcook - what more info would you need? Commented Jul 29 at 4:31
  • @bryanbcook - I've edited the question. Hope you have enough info to work with now. If not, please let me know and I'll provide it to you. Commented Jul 29 at 7:23

1 Answer 1

2
+50

I think this is an issue best solved with trial and error so a bit hard to directly give a complete solution.

I understand that editing the PS script is not posible. Some of the suggestions below suggest editing the PS script. So I guess those are steps that are hard to fulfill

But to solve this I suggest doing the following:

  1. Start with the code without the ip filter that works.

  2. Add the ip filter property and hardcode the array.

    1. If this gives an error still the validation step seem to expect some other format. Maybe AzureResourceManagerTemplateDeployment validates more than just the format. Could also try a newer version of the deploymentParameters.json schema. Should be one from 2019?
  3. Before continueing you could add a validation step in the PS file like "Get-Content .\azuredeploy.parameters.dvlp.json | ConvertFrom-Json". That should validate only the Json formatting.Might be good sine the validation step might validate more than just the correct format. To give more information going forward.

  4. As next step have the ipfilters value be a serialized string with the hardcoded values. That way you wont need the Convert-json that could give extra issues. Prob not a solution unles you can add some step to serialize before running the PS. But can give more information.

  5. If the hardcoded value works well as a parameter add the Convert-json back. You now might need to update the PS. Maybe possible to temporary update just to see if it helps?

    1. If this still fails and the hardcoded works there is probably some issue with the combination of convert-json and the Invoke-EpsTemplate that adds some invisible characters, smart quotes or something like that. I suggest then to find a way to first serialize the ip-filter array and then write it to the parameters file similar to the environment. That way there is les chance of special characters entering.
  6. Also as final note make sure the ip filter content does not have any hidden special character or so.

Hope this information solves your issue!

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

1 Comment

seems that indeed the schema version was the solution here. Apparently it supports arrays under "value". So. here's my global param "ipFilter": { "value": <%= (ConvertTo-Json $ipFilter -Depth 10 -Compress) %> } that gets deserialized correctly. And here's how i pass it to be built per environment { "ipFilter": ["123.123.123.123"] }

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.