Cannot retrieve ARM output array in powershell when using New-AzureRmResourceGroupDeployment - arrays

I have a simple ARM template that outputs an array of elements. I am trying to retrieve said array of elements in powershell, but nothing seems to help. It seems like the deployment result does contain the values of the array, but as soon as I try to navigate to it they become empty strings.
Ideally I want to retrieve the array output from the ARM template into a powershell variable.
ARM template:
{
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
},
"variables": {
"test": [
"value1",
"value2"
]
},
"resources": [
],
"outputs": {
"result": {
"type": "array",
"value": "[variables('test')]"
}
}
}
Everything I have tried:
Powershell code:
$name = ((Get-Date).ToUniversalTime()).ToString('MMdd-HHmmss')
$deploymentResult = New-AzureRmResourceGroupDeployment `
-Name $name `
-ResourceGroupName test4 `
-TemplateFile 'test2.json' `
-Force -Verbose `
-ErrorVariable ErrorMessages
if ($ErrorMessages) {
Write-Output '', 'Template deployment returned the following errors:', #(#($ErrorMessages) | ForEach-Object { $_.Exception.Message.TrimEnd("`r`n") })
}
Write-Output $deploymentResult
Write-Host ($deploymentResult.outputs | Format-Table | Out-String)
Write-Host "Outputs:" $deploymentResult.outputs
Write-Host "Outputs.value:" $deploymentResult.outputs.value
Write-Host "Outputs.values:" $deploymentResult.outputs.values
Write-Host "Outputs[0]:" $deploymentResult.outputs[0]
Write-Host "Outputs[1]:" $deploymentResult.outputs[1]
Write-Host "Outputs[0].value:" $deploymentResult.outputs[0].value
Write-Host "Outputs[1].value:" $deploymentResult.outputs[1].value
Write-Host "Outputs.result:" $deploymentResult.outputs.result
Write-Host "Outputs.result.value:" $deploymentResult.outputs.result.value
Write-Host "Outputs.result.values:" $deploymentResult.outputs.result.values
Write-Host "Outputs.result[0]:" $deploymentResult.outputs.result[0]
Write-Host "Outputs.result[1]:" $deploymentResult.outputs.result[1]
Write-Host "Outputs.result[0].value:" $deploymentResult.outputs.result[0].value
Write-Host "Outputs.result[1].value:" $deploymentResult.outputs.result[1].value
Write-Host "Outputs.result[0][0]:" $deploymentResult.outputs.result[0][0]
Write-Host "Outputs.result.value1:" $deploymentResult.outputs.result.value1
Write-Host "Outputs.result[0].value1:" $deploymentResult.outputs.result[0].value1
Write-Host
Write-Host "Outputs.values:" $deploymentResult.outputs.values
Write-Host "Outputs.result.Count:" $deploymentResult.outputs.result.Count
Write-Host "Outputs.result:" $deploymentResult.outputs.result
Write-Host "Outputs.result[0]:" $deploymentResult.outputs.result[0]
Write-Host "Outputs['result']:" $deploymentResult.outputs['result']
Write-Host
Write-Host "Deployment properties:"
($deploymentResult) | Select-Object -Property *
Write-Host "Outputs properties:"
($deploymentResult.outputs) | Select-Object -Property *
Write-Host "Result properties:"
($deploymentResult.outputs.result) | Select-Object -Property *
Write-Host "['Result'] properties:"
$deploymentResult.outputs['result'] | Select-Object -Property *
Write-Host "[array]['Result'] properties:"
[array]$deploymentResult.outputs['result'] | Select-Object -Property *
Write-Host "Outputs.values properties:"
($deploymentResult.outputs.values) | Select-Object -Property *
Write-Host
ForEach ($res in $deploymentResult.outputs['result']){
Write-Host "Result element:" $res
}
Write-Host
ForEach ($res in $deploymentResult.outputs.values){
Write-Host "Values element:" $res
}
Write-Host
Write-Host "JSON:"
Write-Host "Deployment ConvertTo-Json:"
$json = ($deploymentResult) | ConvertTo-Json
Write-Host $json
Write-Host "Outputs ConvertTo-Json:"
$json = ($deploymentResult.outputs) | ConvertTo-Json
Write-Host $json
Write-Host "Result.Value ConvertTo-Json:"
$json = ($deploymentResult.outputs.result.value) | ConvertTo-Json
Write-Host $json
Powershell output:
VERBOSE: Performing the operation "Creating Deployment" on target "test4".
VERBOSE: 11:00:26 - Template is valid.
VERBOSE: 11:00:26 - Create template deployment '0521-100025'
VERBOSE: 11:00:26 - Checking deployment status in 5 seconds
DeploymentName : 0521-100025
ResourceGroupName : test4
ProvisioningState : Succeeded
Timestamp : 21/05/2020 10:00:26
Mode : Incremental
TemplateLink :
Parameters :
Outputs :
Name Type Value
=============== ========================= ==========
result Array [
"value1",
"value2"
]
DeploymentDebugLogLevel :
Key Value
--- -----
result Microsoft.Azure.Commands.ResourceManager.Cmdlets.SdkModels.DeploymentVariable
Outputs: [result, Microsoft.Azure.Commands.ResourceManager.Cmdlets.SdkModels.DeploymentVariable]
Outputs.value:
Outputs.values: Microsoft.Azure.Commands.ResourceManager.Cmdlets.SdkModels.DeploymentVariable
Outputs[0]:
Outputs[1]:
Outputs[0].value:
Outputs[1].value:
Outputs.result: Microsoft.Azure.Commands.ResourceManager.Cmdlets.SdkModels.DeploymentVariable
Outputs.result.value:
Outputs.result.values:
Outputs.result[0]: Microsoft.Azure.Commands.ResourceManager.Cmdlets.SdkModels.DeploymentVariable
Outputs.result[1]:
Outputs.result[0].value:
Outputs.result[1].value:
Outputs.result[0][0]: Microsoft.Azure.Commands.ResourceManager.Cmdlets.SdkModels.DeploymentVariable
Outputs.result.value1:
Outputs.result[0].value1:
Outputs.values: Microsoft.Azure.Commands.ResourceManager.Cmdlets.SdkModels.DeploymentVariable
Outputs.result.Count: 1
Outputs.result: Microsoft.Azure.Commands.ResourceManager.Cmdlets.SdkModels.DeploymentVariable
Outputs.result[0]: Microsoft.Azure.Commands.ResourceManager.Cmdlets.SdkModels.DeploymentVariable
Outputs['result']: Microsoft.Azure.Commands.ResourceManager.Cmdlets.SdkModels.DeploymentVariable
Deployment properties:
ResourceGroupName : test4
OnErrorDeployment :
DeploymentName : 0521-100025
CorrelationId : 10be00bb-fada-4b54-8749-87460672e9ec
ProvisioningState : Succeeded
Timestamp : 21/05/2020 10:00:26
Mode : Incremental
TemplateLink :
TemplateLinkString :
DeploymentDebugLogLevel :
Parameters : {}
ParametersString :
Outputs : {[result, Microsoft.Azure.Commands.ResourceManager.Cmdlets.SdkModels.DeploymentVariable]}
OutputsString :
Name Type Value
=============== ========================= ==========
result Array [
"value1",
"value2"
]
Outputs properties:
Comparer : System.Collections.Generic.GenericEqualityComparer`1[System.String]
Count : 1
Keys : {result}
Values : {Microsoft.Azure.Commands.ResourceManager.Cmdlets.SdkModels.DeploymentVariable}
IsReadOnly : False
IsFixedSize : False
SyncRoot : System.Object
IsSynchronized : False
Result properties:
Type : Array
Value : {, }
['Result'] properties:
Type : Array
Value : {, }
[array]['Result'] properties:
Type : Array
Value : {, }
Outputs.values properties:
Type : Array
Value : {, }
Result element: Microsoft.Azure.Commands.ResourceManager.Cmdlets.SdkModels.DeploymentVariable
Values element: Microsoft.Azure.Commands.ResourceManager.Cmdlets.SdkModels.DeploymentVariable
JSON:
Deployment ConvertTo-Json:
{
"ResourceGroupName": "test4",
"OnErrorDeployment": null,
"DeploymentName": "0521-100025",
"CorrelationId": "10be00bb-fada-4b54-8749-87460672e9ec",
"ProvisioningState": "Succeeded",
"Timestamp": "\/Date(1590055226689)\/",
"Mode": 0,
"TemplateLink": null,
"TemplateLinkString": null,
"DeploymentDebugLogLevel": null,
"Parameters": {
},
"ParametersString": "",
"Outputs": {
"result": {
"Type": "Array",
"Value": "value1 value2"
}
},
"OutputsString": "\r\nName Type Value \r\n=============== ========================= ==========\r\nresult Array [\r\n \"value1\",\r\n \"value2\"\r\n]\r\n"
}
Outputs ConvertTo-Json:
{
"result": {
"Type": "Array",
"Value": [
"",
""
]
}
}
Result.Value ConvertTo-Json:
[
[
],
[
]
]

The array output is in fact a JArray. Access the result like this: $deploymentResult.outputs.result.value[0].value and $deploymentResult.outputs.result.value[1].value.
More info: PowerShell -- Accessing a JArray inside a JObject

Related

Powershell - json File - select only first value

I've got this json file:
[
{
"group": [313, 312, 313, 311],
"group_name": ["example", "example", "example1"],
"status": ["not_available_time", "no_time"]
}
]
And in Powershell I want the output to be:
313, example, not_available_time
So I always need just the first value.
I've tried this:
$responseJson = ConvertFrom-Json $response.Content
$group = $responseJson.group[0]
$name = $responseJson.group_name[0]
$status = $responseJson.status[0]
That works. But only if there is always more than one value.
For example: if "group_name" in the JSON file would only hold the value "example" (and not ["example", "example", "example1"]) => the output would only be "e" instead of "example". It takes not the first value, but only the first letter if there is only one value.
How can I select the first value?
BR and thanks!
There are 2 safe methods to ensure you're always selecting the first Value, be it an array, a scalar or null:
You can use the array subexpression operator #(), which will ensure that even if the value is $null or [System.Management.Automation.Internal.AutomationNull]::Value the result will always be an array (object[]):
#([System.Management.Automation.Internal.AutomationNull]::Value).GetType().Name # => Object[]
#($null).GetType().Name # => Object[]
As an alternative, Select-Object with the -First 1 argument, the difference is the returned object's Type remains the same:
(1 | Select-Object -First 1).GetType().Name # => Int32
('a', 'b' | Select-Object -First 1).GetType().Name # => String
$null -eq ($null, $null | Select-Object -First 1) # => True
Both alternatives are valid and depends on your use case which one fits your need better:
$json = #'
[
{
"group": [313, 312, 313, 311],
"group_name": "single_element",
"status": null
}
]
'# | ConvertFrom-Json
$json.PSObject.Properties | ForEach-Object {
$arr = #($_.Value)[0]
$slo = $_.Value | Select-Object -First 1
[pscustomobject]#{
'ArraySubExpression' = "[ {0}: $arr ]" -f $_.Name
'Select-Object' = "[ {0}: $slo ]" -f $_.Name
}
}
ArraySubExpression Select-Object
------------------ -------------
[ group: 313 ] [ group: 313 ]
[ group_name: single_element ] [ group_name: single_element ]
[ status: ] [ status: ]

Get specific value from JSON array after comparison using PowerShell?

I want to get specific value once key is matched from JSON array object
Input:
[
{
"Id": "19A7A3C4",
"displayName": "somename",
"tags": [
{
"context": "CONTEXTLESS",
"key": "apple",
"value": "10"
},
{
"context": "CONTEXTLESS",
"key": "orange",
"value": "20"
},
{
"context": "CONTEXTLESS",
"key": "grapes",
"value": "30"
}
]
},
{
"Id": "111111",
"displayName": "somename",
"tags": [
{
"context": "CONTEXTLESS",
"key": "cat",
"value": "10"
},
{
"context": "CONTEXTLESS",
"key": "cat",
"value": "20"
}
]
}
]
I want to get the value of tag where key matches to cat and value matches to 10, I am using below query but getting whole object
$content = Get-Content -Raw -Path "data.json" | ConvertFrom-Json
$content | Where-Object{ $_.tags.key -eq "cat" -and $_.tags.value -eq "10"}
Desired Output: 10
Mathias's answer already shows a clean way to solve the problem.
A similar approach is using the intrinsic method .Where{}:
$tagValues = $content.tags.Where{ $_.key -eq "cat" -and $_.value -eq "10" }.value
$content.tags employs member enumeration to collect all tags properties into an array. .Where{} filters array elements similar to Where-Object. Lastly .value uses member enumeration again to collect the filtered tag values into an array.
Intrinsic methods like .Where{} are supposed to be faster than pipeline commands because they don't involve the overhead of the pipeline machinery.
If you want to keep your original query, you have to deal with nested properties.
Use grouping operator () and dot notation to extract a given property:
$tagValues = ($content | Where-Object{ $_.tags.key -eq "cat" -and $_.tags.value -eq "10"}).tags.value
An alternative is Select-Object with parameter -ExpandProperty (alias -Expand), but it doesn't work as straightforward for nested properties (yet):
$tagValues = $content | Where-Object{ $_.tags.key -eq "cat" -and $_.tags.value -eq "10"} |
Select-Object -Expand tags | Select-Object -Expand value
A more straightforward alternative is ForEach-Object:
$tagValues = $content | Where-Object{ $_.tags.key -eq "cat" -and $_.tags.value -eq "10"} |
ForEach-Object { $_.tags.value }
Enumerate all tags, then use ForEach-Object to grab just the value property of any match:
$content.tags | Where-Object { $_.key -eq "cat" -and $_.value -eq "10"} |ForEach-Object -MemberName value

How to get key value from object inside array JSON using PowerShell?

I am trying to get the key value from object inside JSON array, please also suggest if I need to change my RuleId key structure, below is the output I want
rule = 1300
rulevalue = false
rule = 1304
rulevalue = true
JSON file
{
"Rule": [{
"MPName": "ManagementPackProject",
"Request": "Apply",
"Category": "Rule",
"RuleId": {
"1300": "false",
"1304": "true"
}
}]
}
PowerShell
$Content = Get-Content -Raw -Path "C:\temp\Rule.json" | ConvertFrom-Json -ErrorAction Stop
$Content = $Content.Rule | Where-Object { $_.Request -eq "Apply" }
foreach($item in $Content.RuleId)
{
rule = $item.key
rulevalue = $item.value
process...
}
Are you after something like this?
$JSON = #'
{
"Rule": [{
"MPName": "ManagementPackProject",
"Request": "Apply",
"Category": "Rule",
"RuleId": {
"1300": "false",
"1304": "true"
}
}]
}
'#
$Content = $JSON | ConvertFrom-Json
$Content = $Content.Rule | Where-Object Request -eq 'Apply'
$Output = foreach( $Rule in $Content.RuleId.psobject.Properties ) {
[PSCustomObject]#{
Rule = $Rule.Name
RuleValue = $Rule.Value
}
}
$Output | Format-List
Output:
Rule : 1300
RuleValue : false
Rule : 1304
RuleValue : true

Powershell - Where-Clause multiple JSON Array key

I cannot find any solution why my where-clause in my powershell do not give me the output I would expect.
I have a JSON File looks like this:
[
{
"name": "server1",
"tag": ["App", "Mid-Tier", "User"],
"domain": "domainname",
"alias": ["Alias3"],
"ip": null,
"tcp": 8080,
"active": false,
"apps": ["ARS", "Tomcat"],
"patch": null,
"hotfix": null
},
{
"name": "server2",
"tag": ["Reporting"],
"domain": "domainname",
"alias": ["Alias3"],
"ip": null,
"tcp": 8080,
"active": false,
"apps": ["ARS", "Tomcat"],
"patch": null,
"hotfix": null
},
{
"name": "server3",
"tag": ["DB"],
"domain": "domainname",
"alias": ["Alias1", "Alias2", "Alias3", "Alias4"],
"ip": null,
"tcp": 8080,
"active": true,
"apps": ["SQL"],
"patch": null,
"hotfix": null
},
{
"name": "server5",
"tag": ["Reporting"],
"domain": "domainname",
"alias": ["Alias1"],
"ip": null,
"tcp": 8080,
"active": false,
"apps": ["ARS", "Tomcat"],
"patch": null,
"hotfix": null
}
]
EDIT/UPDATE
Ok I have to give you more details what I am trying to accomplish/understand:
I have 3 Files:
- a JSON File with data like above
- a script storing all functions and variables
- a main script
This is how my function looks like:
function opl_GetHostList {
[CmdletBinding()]
param (
[parameter (
mandatory=$false,
HelpMessage = "Enter alias name.")]
[string[]]
$Aliasname,
[parameter (
mandatory=$false,
HelpMessage = "Enter tag name.")]
[string[]]
$Tagname,
[parameter (
mandatory=$false,
HelpMessage = "Enter app name.")]
[string[]]
$Appname,
[parameter (
mandatory=$false,
HelpMessage = "Enter Active State: Either false or true. Default = true")]
[bool]
$Active = $true,
[parameter (
mandatory=$false,
HelpMessage = "Choose selection Object.")]
[string[]]
$Select
)
$WhereArray = #()
# evaluate these with contains comparison
if ($Aliasname) {$WhereArray += '$Aliasname -contains $_.alias'}
#if ($Tagname) {$WhereArray += '$Tagname -contains $_.tag'}
#if ($Appname) {$WhereArray += '$Appname -contains $_.apps'}
# Build the where array into a string by joining each statement with -and
$WhereString = $WhereArray -Join " -and "
# Create the scriptblock with your final string
$WhereBlock = [scriptblock]::Create($WhereString)
$opl_Hostlist | Where-Object {($WhereBlock.invoke()) -and $Active -eq $_.active} | Select-Object $Select
}
so now comes the part I don't understand.
If I use following in the main.ps1:
$opl_Hostlist | Where-Object {$_.alias -contains "alias3"} | Select-Object name
OUTPUT:
name
----
server1
server2
server3
BUT, if I use it with my function:
opl_GetHostList -Aliasname alias3 -Select name
I get this OUTPUT:
name
----
server1
server2
So why do I not get the "server3" with my second method when I am calling the data with my function?
Very thankfull for any help.
According to the JSON you have posted, you will get ConvertFrom-Json : Invalid array passed in, extra trailing ','. (1018) error. This is because of the unwanted comma after the last item.
And I would suggest to use -in instead of -match operation as Alias property is a list. In your comparison, left operand is a single element and right operand is a list, hence you have to use -in, otherwise -contains.
if ($Aliasname) {
$Aliasname | ForEach-Object{ $WhereArray += "$_ -in `$_.alias"}
}
$WhereArray -Join '-or'

how to create foreach loop in powershell with equal to command

Im starting to learn PowerShell and have been trying to create a foreach loop so that if one of the JSON items has a status other than STARTED, it runs a command using its name as a variable in the executable command. Here is what my json txt file looks like;
{
"UNIT": {
"name": "AB",
"address": "fadasdaer",
"status": "MIA"
},
"UNIT": {
"name": "CD",
"address": "fadasdahsfaaer",
"status": "STARTED"
},
"UNIT": {
"name": "EF",
"address": "9afahegt",
"status": "DEAD"
}
}
And what I am trying to do is read this from my json.txt and get it to run a foreach loop and execute a command where the name is incorporated in the command. I currently have something like this, but my PowerShell understand is limited and it doesnt work...
$JSON = json.txt
$check = $JSON | ConvertFrom-Json
$started=STARTED
foreach($unit in $check.unit){
if ($unit.status -notmatch $started) {
$name=$unit.name
executable.exe start $name
}
}
Any guidance would be greatly appreciated.
Your primary problem is that your JSON is malformed: it defines a single object and then defines its UNIT property multiple times.
You should define it as an array: note the enclosing top-level [...] and the absence of UNIT properties:
[
{
"name": "AB",
"address": "fadasdaer",
"status": "MIA"
},
{
"name": "CD",
"address": "fadasdahsfaaer",
"status": "STARTED"
},
{
"name": "EF",
"address": "9afahegt",
"status": "DEAD"
}
]
With both the JSON input and your other syntax problems corrected:
$JSON = 'json.txt'
$check = Get-Content -Raw $JSON | ConvertFrom-Json
$started = 'STARTED'
foreach ($unit in $check) {
if ($unit.status -notmatch $started) {
$name = $unit.name
executable.exe start $name
}
}
If you cannot fix the JSON at the source, you can transform it yourself before passing it to ConvertFrom-Json:
$check = (Get-Content -Raw $JSON) `
-replace '\A\{', '[' `
-replace '\}\Z', ']' `
-replace '"UNIT": ' | ConvertFrom-JSON

Resources