Extracting variables from calling the value- Getting Error message in condition - json

I am trying to do validation. checking the deployed values with the given values. I extract the vnet values from Azure resources using RestAPI method and convertto-json from Object because of vnet object is giving me empty object (#{value=System.Object[]}). The following is the Json code I am getting:
{
"value": [
{
"properties": "#{virtualNetworkSubnetId=/subscriptions/<XXXX>/resourceGroups/<XXXX>/providers/Microsoft.Network/virtualNetworks/<XXXX>/subnets/<XXXX>; ignoreMissingVnetServiceEndpoint=True; state=Ready}",
"id": "/subscriptions/<XXXX>/resourceGroups/<XXXX>/providers/Microsoft.DBforPostgreSQL/servers/<XXXX>/virtualNetworkRules/<XXXX>",
"name": "<XXXX>",
"type": "Microsoft.DBforPostgreSQL/servers/virtualNetworkRules"
}
]
}
The following powershell command is to compare the value but getting an error saying $vnet.name and $vnet.id is $null
$vnet= ( $vnet | ConvertTo-Json)
It "has this number of vNet Rules defined: $($config.vnetRules.count)"
{
$vnet.count | Should -Be $config.vnetRules.count
}
#$vnet.count is working and giving an success message
foreach ($vnetRule in $config.vNetRules) {
Write-Host $vnet #-> getting Json
Write-Host $vnet.Name #-> return as Empty($null)
Write-Host $vnet.value.Name #-> return as Empty($null)
Write-Host $vnet.id #-> return as Empty($null)
Write-Host $vnet.value.id #-> return as Empty($null)
it "has a vNet rule named: $($vnetRule.ruleName)" {
$vnet.name | Should -Be $vnetRule.ruleName
}
it "has a vNet Rule Subnet ID of: $($vNetRule.subnetId)" {
$vnet.value.id | Should -Be $vNetRule.subnetId
}
}
Returns $null.

In my attempt to recreate your invoke-restmethod output, I used your JSON packet as input to create the $vnet variable.
I believe the problem is you are using Write-host to display the object instead of simply the object name. Write-host will attempt to convert the complex object to a string and hence you see the weird output as you can see below. see the difference when i simply out the object?
Now, $vnet has 4 properties id, name, properties, type and can be invoked as shown.
In you case, you have converted the variable $vnet to json and then attempting to display its properties. But Json does not have any properties, except length. And hence, invoking those properties will give you null.

Related

powershell cant get json key / value

Hello I have a json file below:
{
"supports": {},
"dependencies": {
"AST.NxTestware.main": "1.1.0",
"otherPackagenName": "7.7.7"
},
"frameworks": {
".NETPortable,Version=v4.5,Profile=Profile78": {}
}
}
and I am trying to write a .ps1 powershell script that will look at every item under 'dependencies', check if the name matches, and if so, retrieve the version number.
but I am having trouble parsing through each item in 'dependencies', and cannot retrieve the package name string or version number
My ps1 looks like this:
$targetName = "otherPackagenName"
Write-Host "Trying to get version # of $targetName if it exists inside project.json"
# get json file contents
$json = Get-Content .\project.json | ConvertFrom-Json
# retrieve dependencies
$dependencies = $json.dependencies
Write-Host "dependencies = $dependencies"
# iterate through each dependency
foreach ($dep in $dependencies) {
Write-Host "dep = $dep"
# try to get dependency name (attempt1)
$depName1 = $dep[0]
Write-Host "depName1 = $depName1"
# try to get dependency name (attempt2)
$depName2 = $dep.Name
Write-Host "depName2 = $depName2"
if($depName1 -eq $targetName) {
write-host "Found it! now get version number"
} else {
write-host "Did not find our package"
}
}
output shows it correctly retrieves the json key/value pair, but I cant figure out how to retrieve the package name and version number, as every thing ive tried just returns blank:
Trying to get version # of otherPackagenName if it exists inside project.json
dependencies = #{AST.NxTestware.main=1.1.0; otherPackagenName=7.7.7}
dep = #{AST.NxTestware.main=1.1.0; otherPackagenName=7.7.7}
depName1 = #{AST.NxTestware.main=1.1.0; otherPackagenName=7.7.7}
depName2 =
Did not find our package
my dependencies is coming through like so:
#{AST.NxTestware.main=1.1.0; otherPackagenName=7.7.7}
How can I iterate through this object?
How can I retrieve the key ('AST.NxTestware.main') and value ('1.1.0') for each dependency?
I'm guessing you're looking for this:
$json.Dependencies | ForEach-Object {
$prop = $_.PSObject.Properties
[pscustomobject]#{
DependencyName = $prop.Name
Version = [version] $prop.Value
}
}
You can get an object's property Name and property Value by accessing the object's PSObject member, from there you can reference the Properties property of each object.

Fetching JSON with PowerShell - How do I fetch all keys under multiple arrays?

I'm trying to build a json file that'll be used in our diverse scripts to fetch our server details.
The same script will be used across all our environments that contains multiple products and components. Here's the kind of json file I'm working with:
{
"DEV" : {
"Product1" : [
{"serverName" : "hostname1", "isWebServer" : "true"},
{"serverName" : "hostname2", "isWebServer" : "false"}
],
"Product2" : [
{"serverName" : "hostname3", "isWebServer" : "false"},
{"serverName" : "hostname4", "isWebServer" : "true"}
]
}
}
JSON file is imported into a variable and I'm able to use it successfully.
$jsonFile = Get-Content -Path "C:\temp\test.json" -Raw | ConvertFrom-Json
What I'm trying to do in my PowerShell script is fetch all serverName that is configured with key "isWebServer" : "true"
I can successfully achieve my goal if I'm drilling down on each arrays directly.
($jsonFile.DEV.Product1 | Where-Object {$_.isWebServer -eq "true"}).serverName
However, I'm unable to achieve the same success when I want to fetch all web servers from the whole DEV environment. I'm looking for something like this but that will actually return something.
($jsonFile.DEV | Where-Object {$_.isWebServer -eq "true"}).serverName
How can I fetch everything under all arrays within an object ? I'm used to XPATHs in which I can use wildcards for that kind of scenario but I haven't found the equivalent in JSON.
Here is a possible solution:
$json.DEV.PSObject.Properties.Value.Where{ $_.isWebServer -eq 'true' }.serverName
The hidden (intrinsic) PSObject member allows us to access properties (Product1, Product2, ...) of the DEV object, without having to know their names in advance.
Then, using member access enumeration, we get an array of all Value members of the PSPropertyInfo objects stored in PSObject.Properties.
To filter the server objects, the intrinsic method Where is used to return only the servers matching the given condition. Feel free to use Where-Object instead:
$json.DEV.PSObject.Properties.Value |
Where-Object isWebServer -eq 'true' |
ForEach-Object serverName
Though I prefer the intrinsic methods as they are faster than the pipeline (less overhead) and more succinct to write.
It looks similar to what I did a long time ago. See if this helps.

How to serialize an object in powershell to json and get identical result in PS desktop and core?

Prolog
It turns out that in my case it is important to understand the source of the objects - it is a JSON payload from a REST API response. Unfortunately, JSON -> Object conversion produces different results on PS desktop vs PS core. On desktop the numbers are deserialized into Int32 types, but on core - to Int64 types. From that it follows that I cannot use Export-CliXml, because the binary layout of the objects is different.
Main question
I have a unit test that needs to compare the actual result with an expected. The expected result is saved in a json file, so the procedure is:
Convert the actual result to json string
Read the expected result from disk to string
Compare the actual and the expected as strings
Unfortunately, this scheme does not work because PS desktop ConvertTo-Json and PS core ConvertTo-Json do not produce identical results. So, if the expected result was saved on desktop and the test runs on core - boom, failure. And vice versa.
One way is to keep two versions of jsons. Another way is to use a library to create the json.
First I tried the Newtonsoft-Json powershell module, but it just does not work. I think the problem is that whatever C# library we use, it must be aware of PSCustomObject and alike and treat them specially. So, we cannot just take any C# JSON library.
At this point I am left with having two jsons - one per PS edition, which is kind of sad.
Are there better options?
EDIT 1
I guess I can always read the json, convert to object and then back to json again. That sucks.
EDIT 2
I tried to use ConvertTo-Json -Compress. This eliminates the difference in spacing, but the problem is that for some reason the desktop version translates all the non characters to \u000... representation. The core version does not do it.
Please, observe:
Desktop
C:\> #{ x = "'a'" } |ConvertTo-Json -Compress
{"x":"\u0027a\u0027"}
C:\>
Core
C:\> #{ x = "'a'" } |ConvertTo-Json -Compress
{"x":"'a'"}
C:\>
Now the core version has the flag -EscapeHandling, so:
C:\> #{ x = "'a'" } |ConvertTo-Json -Compress -EscapeHandling EscapeHtml
{"x":"\u0027a\u0027"}
C:\>
Bingo! Same result. But now this code does not run on the desktop version, which does not have this flag. More massaging is needed. I will check if that is the only problem.
EDIT 3
It is impossible to reconcile the differences between the core and the desktop versions without expensive post processing. Please, observe:
Desktop
C:\> #{ x = '"a"';y = "'b'" } |ConvertTo-Json -Compress
{"y":"\u0027b\u0027","x":"\"a\""}
C:\>
Core
C:\> #{ x = '"a"';y = "'b'" } |ConvertTo-Json -Compress -EscapeHandling EscapeHtml
{"y":"\u0027b\u0027","x":"\u0022a\u0022"}
C:\> #{ x = '"a"';y = "'b'" } |ConvertTo-Json -Compress
{"y":"'b'","x":"\"a\""}
C:\>
Any suggestions on how to salvage the json approach?
EDIT 4
The Export-CliXml approach does not work too, because of the differences between the PS versions.
Desktop
C:\> ('{a:1}' | ConvertFrom-Json).a.gettype()
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True Int32 System.ValueType
C:\>
Core
C:\> ('{a:1}' | ConvertFrom-Json).a.gettype()
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True Int64 System.ValueType
C:\>
So the same JSON is represented using different numeric types - Int32 in desktop and Int64 in core. That puts to bed the option of using Export-CliXml.
Unless I am missing something.
I believe there is no other choice, but do the double conversion - json -> object -> json and then I will have two jsons created on the same PS edition. That sucks big time.
On converting from the original JSON, use the third-party Newtonsoft.Json PowerShell wrapper's ConvertFrom-JsonNewtonsoft cmdlet - this should ensure cross-edition compatibility (which the built-in ConvertFrom-Json does not guarantee across PowerShell editions, because Windows PowerShell uses a custom parser, whereas PowerShell [Core] v6+ uses Newtonsoft.json up to at least v7.1, though a move to the new(ish) .NET Core System.Text.Json API is coming).
Important: ConvertFrom-JsonNewtonsoft returns (arrays of) nested ordered hashtables ([ordered] #{ ... }, System.Collections.Specialized.OrderedDictionary), unlike the nested [pscustomobject] graphs that the built-in ConvertFrom-Json outputs. Similarly, ConvertTo-JsonNewtonsoft expects only (arrays of) hashtables (dictionaries) as input, and notably does not support [pscustomobject] instances, as you've learned yourself.
Caveat: As of this writing, the wrapper module was last updated in May 2019, and the version of the underlying bundled Newtonsoft.Json.dll assembly is quite old (8.0, whereas 12.0 is current as of this writing). See the module's source code.
Note that in order to parse JSON obtained from a RESTful web service manually, you mustn't use Invoke-RestMethod, as it implicitly parses and returns [pscustomobject] object graphs. Instead, use Invoke-WebRequest and access the returned response's .Content property.
On converting to a format suitable for storing on disk, you have two options:
(A) If you do need the serialized format to be JSON also, you must convert all [pscustomobject] graphs to (ordered) hashtables before passing them to ConvertTo-JsonNewtonsoft.
See below for function ConvertTo-OrderedHashTable, which does just that.
(B) If the specific serialization format isn't important, i.e. if all that matters is that the formats are identical across PowerShell editions for the purpose of comparison, no extra works is needed: use the built-in Export-Clixml cmdlet, which can handle any type and produces PowerShell's native, XML-based serialization format called CLIXML (as notably used in PowerShell remoting), which should be cross-edition-compatible (at least with v5.1 on the Windows PowerShell side and as of PowerShell [Core] v7.1, both of which use the same version of the serialization protocol, 1.1.0.1, as reported by $PSVersionTable.SerializationVersion).
While you could re-convert such a persisted file to objects with Import-Clixml, the potential loss of type fidelity on deserialization makes comparing the serialized (CLIXML) representations advisable.
Also note that as of PowerShell v7.1 there is no cmdlet-based way to create an in-memory CLIXML representation, so you'll have to use the PowerShell API directly for now: System.Management.Automation.PSSerializer.Serialize. However, providing in-memory counterparts to Import-CliXml / Export-CliXml in the form of ConvertFrom-CliXml / ConvertTo-CliXml cmdlets has been green-lighted as a future enhancement.
Re (A): Here's function ConvertTo-OrderedHashtable, which converts (potentially nested) [pscustomobject] objects to ordered hashtables while passing other types through, so you should be able to simply insert it into a pipeline as follows:
# CAVEAT: ConvertTo-JsonNewtonSoft only accepts a *single* input object.
[pscustomobject] #{ foo = 1 }, [pscustomobject] #{ foo = 2 } |
ConvertTo-OrderedHashtable |
ForEach-Object { ConvertTo-JsonNewtonSoft $_ }
function ConvertTo-OrderedHashtable {
<#
.SYNOPSIS
Converts custom objects to ordered hashtables.
.DESCRIPTION
Converts PowerShell custom objects (instances of [pscustomobject]) to
ordered hashtables (instances of [System.Collections.Specialized.OrderedDictionary]),
which is useful for to-JSON serialization via the Newtonsoft.JSON library.
Note:
* Custom objects are processed recursively.
* Any scalar non-custom objects are passed through as-is.
* Any (non-dictionary) collections in property values are converted to
[object[]] arrays.
.EXAMPLE
1, [pscustomobject] #{ foo = [pscustomobject] #{ bar = 'none' }; other = 2 } | ConvertTo-OrderedHashtable
Passes integer 1 through, and converts the custom object to a nested ordered
hashtable.
#>
[CmdletBinding()]
param(
[Parameter(ValueFromPipeline)] $InputObject
)
begin {
# Recursive helper function
function convert($obj) {
if ($obj -is [System.Management.Automation.PSCustomObject]) {
# a custom object: recurse on its properties
$oht = [ordered] #{ }
foreach ($prop in $obj.psobject.Properties) {
$oht.Add($prop.Name, (convert $prop.Value))
}
return $oht
}
elseif ($obj -isnot [string] -and $obj -is [System.Collections.IEnumerable] -and $obj -isnot [System.Collections.IDictionary]) {
# A collection of sorts (other than a string or dictionary (hash table)), recurse on its elements.
return #(foreach ($el in $obj) { convert $el })
}
else {
# a non-custom object, including .NET primitives and strings: use as-is.
return $obj
}
}
}
process {
convert $InputObject
}
}
Re (B): A demonstration of the Export-CliXml approach (you can run this code from either PS edition):
$sb = {
Install-Module -Scope CurrentUser Newtonsoft.json
if (-not $IsCoreClr) {
# Workaround for PS Core's $env:PSModulePath overriding WinPS'
Import-Module $HOME\Documents\WindowsPowerShell\Modules\newtonsoft.json
}
#'
{
"results": {
"users": [
{
"userId": 1,
"emailAddress": "jane.doe#example.com",
"date": "2020-10-05T08:08:43.743741-04:00",
"attributes": {
"height": 165,
"weight": 60
}
},
{
"userId": 2,
"emailAddress": "john.doe#example.com",
"date": "2020-10-06T08:08:43.743741-04:00",
"attributes": {
"height": 180,
"weight": 72
}
}
]
}
}
'# | ConvertFrom-JsonNewtonsoft | Export-CliXml "temp-$($PSVersionTable.PSEdition).xml"
}
# Execute the script block in both editions
Write-Verbose -vb 'Running in Windows PowerShell...'
powershell -noprofile $sb
Write-Verbose -vb 'Running in PowerShell Core...'
pwsh -noprofile $sb
# Compare the resulting CLIXML files.
Write-Verbose -vb "Comparing the resulting files: This should produce NO output,`n indicating that the files have identical content."
Compare-Object (Get-Content 'temp-Core.xml') (Get-Content 'temp-Desktop.xml')
Write-Verbose -vb 'Cleaning up...'
Remove-Item 'temp-Core.xml', 'temp-Desktop.xml'
You should see the following verbose output:
VERBOSE: Running in Windows PowerShell...
VERBOSE: Running in PowerShell Core...
VERBOSE: Comparing the resulting files: This should produce NO output,
indicating that the files have identical content.
VERBOSE: Cleaning up...

Yet another loop JSON object using Powershell

I am not able to twist my head into understanding how to get Powershell to loop the entire JSON Structure, it wont' loop the System.Object[]
$x = ConvertFrom-Json '{
"Car companies": {
"Name of Company": "Ford",
"Cars": [{
"Name of car": "Ranger",
"Config": "Pickup"
},
{
"Name of car": "Puma",
"Config": "Hatchback"
}]
}
}'
foreach( $rootProperty in #($x.psobject.properties | where-object {$_.MemberType -eq "NoteProperty"}) ) {
write-host " - '$($rootProperty.Name)' = '$($rootProperty.Value)'"
foreach( $childProperty in #($rootProperty.Value.psobject.properties ) ) {
write-host "'$($childProperty.Name)' = '$($childProperty.Value)'"
}
}
Outut I get now is just
- 'Brand' = '#{Name of Brand=Ford; Cars=System.Object[]}'
Name of Brand' = 'Ford'
Cars' = ' '
...as a follop How to iterate through a unknown JSON data/object?
tl;dr
You're seeing a bug that unexpectedly string-expands the Cars property value's array elements to the empty string.
A simple workaround - for display purposes only - is to pipe the property value to Out-String to get the usual display representation:
"'$($childProperty.Name)' = '$($childProperty.Value | Out-String)'"
You're seeing a bug in how arrays of [pscustomobject] instances are stringified (as of PowerShell Core 7.0.0-preview.6):
Generally, PowerShell arrays are stringified by joining the stringified element representations with the separator specified in the $OFS preference variable, which defaults to a space char.
Normally, [pscustomobject] instances have a string representation that resembles a hashtable literal (but isn't one); e.g.:
PS> $custObj = [pscustomobject] #{ foo = 'bar' }; "$custObj"
#{foo=bar} # string representation that *resembles* a hashtable literal
Unexpectedly - and this is the bug - when custom objects are the elements of an array, they stringify to the empty string, which is what you saw:
PS> $custObj = [pscustomobject] #{ foo = 'bar' }; $arr = $custObj, $custObj; "[$arr]"
[ ] # !! Bug: custom objects stringified to empty strings, joined with a space
This is an indirect manifestation of a long-standing bug reported in this GitHub issue: that is, elements of an array being stringified are stringified by calls to their .ToString() method, and calling .ToString() on custom objects unexpectedly yields the empty string (unlike the string representation you get when you directly reference a single custom object in an expandable string, as shown above).

How to pass hashtables / objects from template to runbooks

I have written a template and accompanying runbook. The template triggers the runbook. The template and the runbook are working fine, until I try and pass an object to one of the runbook's parameters. The Azure error is:
Error
"content":
{
"status": "Failed",
"error":
{
"code": "DeploymentFailed",
"message": "At least one resource deployment operation failed. Please list deployment operations for details. Please see https://aka.ms/arm-debug for usage details.",
"details":
[
{
"code": "BadRequest",
"message": "{\r\n \"code\": \"BadRequest\",\r\n \"message\": \"{\\\"Message\\\":\\\"The request is invalid.\\\",\\\"ModelState\\\":{\\\"job.properties.parameters.MyTags\\\":[\\\"An error has occurred.\\\"]}}\"\r\n}"
}
]
}
}
I'm not sure what is meant by ModelState, however the job.properties.parameters.MyTags is pointing to the troublesome parameter.
I am sure it is something to do with the datatype. It may be because the parameter is being passed as a JSON object and the runbook cannot understand it. I'm more used to passing objects from PowerShell to templates.
Runbook (Update-ResourceGroupTags.ps1)
For testing, the runbook looks like:
param
(
[string]$ResourceGroupId,
$MyTags
)
Write-Output "ResourceGroupId: $ResourceGroupId"
Write-Output "MyTags: $($MyTags | Out-String)"
Write-Output "MyTagsType: $($MyTags.GetType() | Out-String)"
No authentication as it's not required yet and I have deliberately not typed $MyTags, although I have tried [object] and using `[parameter(Mandatory = $true)] just in case there's an unwritten / missed rule.
Goal
In brief, I create a complex object as a variable in the template and I want to pass it to a runbook's parameter as a single object. The values are dynamic and not known until deploy-time. Having to specify each parameter (test 8) in the runbook breaks this requirement.
I start a template with the New-AzureRmResourceGroupDeployment cmdlet.
I create a "complex object" variable:
"rolesTagObject": {
"db": "TestVm1",
"Server": "TestVm1",
"Client": "TestVm1"
}
The template runs a resource of "type" : "jobs".
The Runbook, run by jobs has a parameter MyTags which needs to take roleTagObject which I pass to jobs.properties.parameters.MyTags thus:
,
"parameters": {
"MyTags": "[variable('roleTagObject')]"
}
This is what's not working. If I break it down into each key (Test 8) it works.
My initial thought is to convert it to a single string and pass it to the runbook using json() function, but I don't know of a way to do that in a template.
Template (testRunRunbook.json)
I've put the template in a GIST so as not to make this question any longer.
https://gist.github.com/arcotek-ltd/7c606540980a45a3a7915ccae2e0b140
The template has been written so I can copy out the "resources":[] section into a different template. Hence why some variables and parameters may appear to be oddly named. As I said, the template works, apart from this issue.
PowerShell
I am calling the template with PowerShell, thus:
$Ticks = (Get-Date).Ticks.ToString()
$RGID = (Get-AzureRmResourceGroup -Name "MyResourceGroup").ResourceId
$MyTags = #{"TestTag2"="TestValue2"}
$JsonTagsHash = ($MyTags | ConvertTo-Json -Depth 20 | Out-String) -replace '\s',''
$TemplateParametersObject = #{
currentDateTimeInTicks = $Ticks
runbookParameters = #{
ResourceGroupId = $RGID
#"MyTags" = $MyTags #$JsonTagsHash
}
}
New-AzureRmResourceGroupDeployment `
-Name "Test_Runbook" `
-ResourceGroupName "MyResourceGroup" `
-Mode Incremental `
-DeploymentDebugLogLevel All `
-Force `
-TemplateFile "D:temp\testRunRunbook.json" `
-Verbose `
-TemplateParameterObject $TemplateParametersObject
I've tried the following tests:
Test 1
Uncomment $TemplateParametersObject.runbookParameters.MyTags:
$MyTags = #{"TestTag1"="TestValue1"}
$TemplateParametersObject = #{
currentDateTimeInTicks = $Ticks
runbookParameters = #{
ResourceGroupId = $RGID
MyTags = $MyTags
}
}
Result Fail - See error above.
Test 2
Replace $MyTags with $JsonTagsHash:
$MyTags = #{"TestTag2"="TestValue2"}
$JsonTagsHash = ($MyTags | ConvertTo-Json -Depth 20 | Out-String) -replace '\s',''
$TemplateParametersObject = #{
currentDateTimeInTicks = $Ticks
runbookParameters = #{
ResourceGroupId = $RGID
MyTags = $JsonTagsHash
}
}
Result: PASS Works as expected. Parameters are passed to runbook.
Test 2 works, but I need to be able to pass the parameters to the runbook that are generated inside the template at runtime. In other words, I can't use PowerShell. Inside the template at job.properties.parameters (Line 103 in the gist)
Test 3
To prove it's MyTags causing the issue, take it out completely:
"parameters": {
"ResourceGroupId": "[parameters('runbookParameters').ResourceGroupId]"
}
Result: No error, however, myTags are not passed (obviously).
Test 4
Create a variable object and pass that to the parameter:
"variables" : {
"rolesTagObject": {
"db": "TestVm1",
"Server": "TestVm1",
"Client": "TestVm1"
}
}
And back in job.properties.parameters:
"parameters": {
"ResourceGroupId": "[parameters('runbookParameters').ResourceGroupId]",
"MyTags": "[variables('rolesTagObject')]"
}
Result Fail - See error above.
Test 5
Try directly:
"parameters": {
"ResourceGroupId": "[parameters('runbookParameters').ResourceGroupId]",
"MyTags": {
"testTag5" : "testValue5"
}
}
Result Fail - See error above.
Test 6
Use the json() template function. Not that I expect it to work as it needs a string.
"parameters": {
"ResourceGroupId": "[parameters('runbookParameters').ResourceGroupId]",
"MyTags": "[json(variables('rolesTagObject'))]"
}
Result Fail. As predicted:
'The template language function 'json' expects an argument of type 'string'. The provided value is of type 'Object'.
Test 7
Try with the example provided my MS for json() template function:
"parameters": {
"ResourceGroupId": "[parameters('runbookParameters').ResourceGroupId]",
"MyTags": "[json('{\"a\": \"b\"}')]"
}
Result Fail- See error above. Interesting! But why? The working PowerShell test suggests it needs to be a JSON object. Is this not what json() does?
Test 8
Breaking out the object into individual key / values:
"parameters": {
"db": "[variables('rolesTagObject').db]",
"server": "[variables('rolesTagObject').server]",
"client": "[variables('rolesTagObject').client]"
}
I also have to change the parameters in the runbook from one to three.
Result Pass - but the idea of being able to pass an object is that it can have any number of key / value pairs. This way, not only does the variable and parameter have to be hardcoded, so does the runbook. Not very flexible.
I'm out of ideas. Any suggestions please?
The automation job parameters does not understand complex objects so you would want to pass in the parameter as a string and then convert back to Json within your runbook.
In your example, you could put quotes around the json object $MyTags so that it comes through as a string.
From
$TemplateParametersObject = #{
currentDateTimeInTicks = $Ticks
runbookParameters = #{
ResourceGroupId = $RGID
"MyTags" = $MyTags
}
}
to
$TemplateParametersObject = #{
currentDateTimeInTicks = $Ticks
runbookParameters = #{
ResourceGroupId = $RGID
"MyTags" = "$MyTags"
}
}
Then inside your runbook you could do
$MyTags = ConvertFrom-Json $MyTags
to get it back to a json object that you can use as needed.
If you are converting an object inside a template to a string, then you can just use the string function https://learn.microsoft.com/en-us/azure/azure-resource-manager/resource-group-template-functions-string#string
Example as shown in the comments:
"myParams": {
"Hello": "World",
"Location": {
"Country": "USA",
"State": "WA"
}
},
and then you can pass in this template object by just converting it to a string:
"properties": {
"runbook": {
"name": "[variables('runbookName')]"
},
"parameters": {
"ResourceGroupId": "[variables('myParams').Hello]",
"MyTags": "[String(variables('myParams'))]"
}
If the string function doesn't work, please let me know.
Hope this help,
Eamon