John Walker
John Walker

Reputation: 1

Can't run powershall script

I have a powershell script like that:

Param(
    [Parameter(Mandatory=$True)]
    [String]
    $NEWPARAMVALUE,

    [Parameter(Mandatory=$True)]
    [String]
    $PARAM_NAME
)
$FILEPATH = “arm_template.json”
$JSON = Get-Content $FILEPATH | Out-String | ConvertFrom-Json
$JSON.$PARAM_NAME = $NEWPARAMVALUE
$JSON | ConvertTo-Json -Depth 10 | Set-Content $FILEPATH

When I used this script without $PARAM_NAME variable (I just put a parameter as a plain text, example: $JSON.resources.properties.parameters.daasServiceBaseUrl.defaultValue) - it worked.

I tried different ways:

  1. $JSON.resources.$PARAM_NAME (where $PARAM_NAME = 'properties.parameters.daasServiceBaseUrl.defaultValue')

  2. $JSON"."$PARAM_NAME"

I think that's because of the point between two env variables. Could please someone help with it?

Update:

Error that I get from Azure DevOps:

The property | 'properties.parameters.parametername.defaultValue' cannot | be found on this object. Verify that the property exists and | can be set.

How I tried it manually:

$env:PARAM_NAME="resources.properties.parameters.daasServiceBaseUrl.defaultValue"


./test.ps1 -NEWPARAMVALUE "[parameters('DaaS_Contract_Daily_Trigger_properties_Daas-UI-to-Contract_parameters_daasServiceBaseUrl')]" -PARAM_NAME $env:PARAM_NAME

What I got:

Line |
  12 |  $JSON.$PARAM_NAME = $NEWPARAMVALUE
     |  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     | Exception setting "resources.properties.parameters.daasServiceBaseUrl.defaultValue": "The property
     | 'resources.properties.parameters.daasServiceBaseUrl.defaultValue' cannot be found on this object. Verify that the property exists and can be
     | set."

My JSON:

{
    "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
    "contentVersion": "1.0.0.0",
    "parameters": {
        "dataFactoryName": {
            "type": "string",
            "defaultValue": ""
        },
        "location": {
            "type": "string",
            "defaultValue": "[resourceGroup().location]",
            "metadata": {
                "description": "Data Factory location matches Resource group location"
            }
        },
        "project": {
            "type": "string",
            "defaultValue": "ct",
            "metadata": {
                "description": "Azure Tag used to track projects"
            }
        },
        "environment": {
            "type": "string",
            "defaultValue": "default",
            "metadata": {
                "description": "Azure Tag used to track environments"
            }
        },
        "sleepTime": {
            "type": "int",
            "defaultValue": "1"
        },
        "DaaS_Contract_Daily_Trigger_properties_Daas-UI-to-Contract_parameters_daasServiceBaseUrl": {
            "type": "string",
            "defaultValue": "http://digital-daas-service-om-sqa.nplabsusk8s.com/daas"
        }
    },
    "resources": [
        {
            "apiVersion": "2018-06-01",
            "name": "[parameters('dataFactoryName')]",
            "location": "[parameters('location')]",
            "tags": {
                "project": "[parameters('project')]",
                "environment": "[parameters('environment')]"
            },
            "type": "Microsoft.DataFactory/factories",
            "identity": {
                "type": "SystemAssigned"
            },
            "properties": {}
        },
        {
            "name": "[concat(parameters('dataFactoryName'), '/pipeline1')]",
            "type": "Microsoft.DataFactory/factories/pipelines",
            "apiVersion": "2018-06-01",
            "properties": {
                "activities": [
                    {
                        "name": "Wait1",
                        "type": "Wait",
                        "dependsOn": [],
                        "userProperties": [],
                        "typeProperties": {
                            "waitTimeInSeconds": "[parameters('sleepTime')]"
                        }
                    },
                    {
                        "name": "Copy Delivery Request",
                        "type": "Copy",
                        "dependsOn": [
                            {
                                "activity": "Delete old Req File",
                                "dependencyConditions": [
                                    "Succeeded"
                                ]
                            }
                        ],
                        "policy": {
                            "timeout": "7.00:00:00",
                            "retry": 0,
                            "retryIntervalInSeconds": 30,
                            "secureOutput": false,
                            "secureInput": false
                        },
                        "userProperties": [],
                        "typeProperties": {
                            "source": {
                                "type": "RestSource",
                                "httpRequestTimeout": "00:01:40",
                                "requestInterval": "00.00:00:00.010",
                                "requestMethod": "GET"
                            },
                            "sink": {
                                "type": "JsonSink",
                                "storeSettings": {
                                    "type": "AzureBlobFSWriteSettings"
                                },
                                "formatSettings": {
                                    "type": "JsonWriteSettings",
                                    "quoteAllText": true
                                }
                            },
                            "enableStaging": false
                        },
                        "inputs": [
                            {
                                "referenceName": "Rest_Json_File",
                                "type": "DatasetReference",
                                "parameters": {
                                    "relativeURL": {
                                        "value": "@{pipeline().parameters.daasServiceRelURL}/@{variables('createdDate')}",
                                        "type": "Expression"
                                    },
                                    "BaseUrl": {
                                        "value": "@pipeline().parameters.daasServiceBaseUrl",
                                        "type": "Expression"
                                    }
                                }
                            }
                        ]
                    }
                ],
                "parameters": {
                    "deliveryReqFileName": {
                        "type": "string",
                        "defaultValue": "delivery_request.json"
                    },
                    "daasServiceBaseUrl": {
                        "type": "string",
                        "defaultValue": "http://digital-daas-service-om-sqa.nplabsusk8s.com/daas"
                    }
                },
                "annotations": []
            },
            "dependsOn": []
        }
    ]
}

Upvotes: 0

Views: 99

Answers (1)

Mathias R. Jessen
Mathias R. Jessen

Reputation: 174835

When you pass a string "a.b.c.d" as the right-hand operand of the . member access operator, PowerShell will attempt to resolve the exact member name a.b.c.d, not "a, then b, then c, then d".

While this could be solved by executing Invoke-Expression against "${JSON}.${PARAM_NAME}", that also opens your script up to arbitrary execution of whatever the user might pass in as $PARAM_NAME.

To avoid that, you'll need to manually:

  1. Split $PARAM_NAME into individual member names, like $names = $PARAM_NAME.Split('.'), then
  2. Resolve each property name except for the last one manually
  3. Assign to $resolvedObject."$last"

Here's how that might look in a generic helper function that takes care of step 2 and 3:

function Set-PropertyChained
{
  param(
    [Parameter(Mandatory = $true, ValueFromPipeline = $true, Position = 0)]
    [ValidateScript({$_ -isnot [ValueType]})]
    [object]$RootObject,
    
    [Parameter(Mandatory = $true, Position = 1)]
    [string[]]$MemberChain,

    [Parameter(Mandatory = $true, Position = 2)]
    [object]$Value,

    [switch]$PassThru
  )

  begin {
    $resolutionChain = @($MemberChain |Select -SkipLast 1)
    $terminalMember  = $($MemberChain |Select -Last 1)
  }

  process {
    $obj = $RootObject

    # Member resolution up until the parent of the last property
    foreach($memberName in $resolutionChain){
      Write-Vebose "Resolving $memberName on $obj"
      $obj = $obj.$memberName
    }

    # Set the value of the last property on its parent
    Write-Vebose "Setting $memberName on $obj to $Value"
    $obj.$terminalMember = $Value

    if($PassThru){
      # Write modified object back if requested
      Write-Output $RootObject -NoEnumerate
    }
  }
}

Then in your script:

Param(
    [Parameter(Mandatory=$True)]
    [String]
    $NEWPARAMVALUE,

    [Parameter(Mandatory=$True)]
    [String]
    $PARAM_NAME
)

$FILEPATH = “arm_template.json”
$JSON = Get-Content $FILEPATH | Out-String | ConvertFrom-Json

# Step 1, split the resolution chain into individual components
$MemberNames = $PARAM_NAME.Split('.')

# Step 2 + 3, resolve n-2 members in chain, then assign to n-1
$JSON | Set-PropertyChained -MemberChain $MemberNames -Value $NEWPARAMVALUE

$JSON | ConvertTo-Json -Depth 10 | Set-Content $FILEPATH

Upvotes: 2

Related Questions