I have the JSON block below that I am trying to parse with PowerShell. What the JSON is for is to denote permissions that an AD group should be given on an Azure App Registration. An application will have multiple groups assigned to it and each group will have a unique set of roles (permissions). The full JSON file has several of these application blocks.
Basically, what I want to be able to do with PowerShell is to take the application, query Azure AD to get the application resource and then modify the resource to assign each group to the app with its roles using New-AzureADGroupAppRoleAssignment.
Conventional thinking would be the I need a loop nested inside of a loop nested inside of a loop where the first loop grabs the application block, then the second loop grabs the groupname block and then the third loop grabs the array of roles for that group.
"application":
[
{
"groupname": "adgroup1",
"roles": [
"Permission1",
"Permission2",
"Permission3",
"Permission4",
"Permission5",
"Permission6"
]
},
{
"groupname": "adgroup2",
"roles": [
"Permission1",
"Permission2",
"Permission4",
"Permission5",
"Permission6"
]
}
Something like this should get you started
$json = SomethingThatGetsJson | ConvertFrom-Json
$json.application | % {
$data = $_
Write-Host $data.groupname
$data.roles | % {
$role = $_
Write-Host $role
}
}
% is shorthand for ForEach-Object if you want to get some more details on that. $_ is the element value in the current iteration of the enumeration.
So long as the JSON returned is valid you should use the ConvertFrom-Json cmdlet, It will convert the JSON string in to a proper object you can dot walk through and run your other standard cmdlets against like select, where, foreach etc
Related
I've recently been working on a project that needs me to compare two JSON files and return the differences between both in PowerShell. I recently started to learn the language, and I've come across multiple issues.
I've tried checking all over the internet and forums from what I could understand of my issue, but nothing really worked.
I tried using Compare-Object but it returned me the whole JSON as a difference, transforming them into PsObjects, it kinda worked but I couldn't get the full path and broke at some point, etc...
My final attempt was to iterate over each property of my object and when I would encounter an object, I would simply "open" the object / "redirect" myself in it and so on... (basically, iterating in object & sub-objects).
It's almost working, thus I'm not sure why I've got an issue. Here's the code sample :
(note that $obj is one single PowerShell Object coming from a foreach loop (foreach $obj in $Obj) and $template is another PS object)
function Compare-Templates($obj, $template, $current_path)
{
ForEach ($prop in $obj.PsObject.Properties) {
Write-Host "$($prop.Name):$($prop.value)" -ForegroundColor Magenta
Write-Host "$($current_path)$($prop.Name)" -ForegroundColor DarkBlue
Write-Host ""
if ($ObjectTypesName -contains $prop.TypeNameOfValue) {
Write-Host "$($prop.Value) is a PsObject!" -ForegroundColor DarkRed
Write-Host ""
Write-Host "-------- Nested in $($prop.Name) --------" -ForegroundColor Yellow
Compare-Templates $prop.value $template ($current_path + "$($prop.Name)/")
Write-Host "-------- Out Nested --------" -ForegroundColor Yellow
Write-Host ""
}
}
}
Output:
-------- Nested in oneObject --------
firstValue:1
oneObject/firstvalue
It would give me the value of the variables found and its path (I need it later on).
The issue :
In my JSON file, I can have an array inside a variable such as :
"Object": [ <-------- the array that gives the weird `SyncRoot` variables over and over
{
"anotherObject": {
"firstValue": false,
"secondValue": false
},
[...]
The program understands that the array can be accessed (it even gives me the content in "preview", e.g. object:#(anotherObject=))
In my debug, it would say :
-------- Nested in OBJ--------
Count:1
OBJ/Count
Length:1
OBJ/Length
LongLength:1
OBJ/LongLength
Rank:1
OBJ/Rank
SyncRoot:#{object1=; object2=; object3=System.Object[]; object4=; object5=; object6=; object7=; object8=System.Object[]; object9=System.Object[]}
OBJ/SyncRoot
(Note that for security reasons I changed all variable names, but there is still a cohesion between them)
Everything found in the object is something which doesn't exist in my object, and I'm not sure where it's from (probably the array's "default" variable).
My question is : How could I access the array which is nested in my object, without looping indefinitely over the SyncRoot object?
Or should I use this hand-made method or is there an actual function / simple way to iterate over a PowerShell object, nest with other PowerShell objects OR comparing JSONs that would give me the full "path" of a variable with different values?
Note that it can iterate indefinitely such as OBJ_which_has_an_array_like_the_example_above/SyncRoot/SyncRoot/SyncRoot/SyncRoot/SyncRoot/SyncRoot/Length and so on...
My aim is to use Azure Data Factory to copy data from one place to another using REST API.
The first part of the copying is using the ForEach activity to select parameters from a nested JSON/array. I have had problems with this nested JSON because of various error messages in the ForEach activity.
My JSON is of the following form:
(
{
"key_1": "value_1",
"key_2": [
"value_2_1",
"value_2_2"
]
}
)
and first I'm setting the #json conversion function in front of it:
#json(
'{
"key_1": "value_1",
"key_2": [
"value_2_1",
"value_2_2"
]
}'
)
Here you can see the Execute Pipeline object and its parameters:
I am setting this JSON as a parameter in the Execute Pipeline object. I am setting its type as an "Array". (For the record, I have still had similar error messages even though I have tried to change the parameter type to "String" or "Object".)
The ForEach activity is used to select an item from the nested JSON, which is written into a parameter.
This immediately produces the following error in the Execute Pipeline activity:
Operation on target... ...failed: The function 'length' expects its parameter to be an array or a string. The provided value is of type 'Object'.
So, even though I set the JSON parameter type to "Array", it is changed into "Object" when I debug the pipeline activity. See the error below:
Next, I tried to use the ADF #createArray function before my JSON text.
#createArray(
'{
"key_1": "value_1",
"key_2": [
"value_2_1",
"value_2_2"
]
}'
)
When debugging, the forEach activity throws me an error in the first Copy data activity:
The expression 'concat(item().SELECTING_key_1_FROM_MY_JSON))' cannot be evaluated because property 'key_1' cannot be selected. Property selection is not supported on values of type 'String'.
Please help me, what I am doing wrong in my attempts of converting the JSON to an array? What should I change in my code?
So far I have tried changing the parameter type and using various functions in the JSON dynamic content but with no luck.
update:
If you want change the filename in Copy Data --> Sink.
You can key in the dynamic content #concat(pipeline().parameters.Pip_Object.key_1,'.json') to rename the file.
Please correct me if I understand you wrong.
First, we should use Parameters to store the Json array as follows. Because Variables are not support to store the Json array. Variables are only support to store simple data type such as ["1","2","3"...].
Json array format should as follows:
[{"key_1": "value_1"},{"key_2": ["value_2_1","value_2_2"]}]
I created a simple test here. Then ForEach the Json array:
Inside ForEach1 activity, pass #item() to the object type parameter Pip_Object.
The Input is as follows:
Add dynamic content above using any combination of expressions, functions and system variables at for each settings
#activity('Get Metadata1').output.childItems
We ended up using two pipelines: a generic one and a specific one.
The generic one has the JSON as a parameter (JSON_PARAMETER), and we set it a default value in the following form
[{"key_1":"value_1","key_2":["value_2"]},{"key_1":"value_3","key_2":["value_2"]}, ...etc. ...}]
The generic pipeline has a forEach loop, in which that mentioned JSON parameter is called in Settings -> Items:
#JSON(pipeline().parameters.JSON_PARAMETER)
In the specific pipeline, there is an Execute Pipeline activity, where the JSON_PARAMETER is found in Settings -> Parameters. If the default value of JSON_PARAMETER is used, the field is left blank. If we want to change the parameter, before Execute Pipeline, we put a Set Variable activity where we change the Variables -> Value to:
#concat('
[
{"key_1":"value_1",
"key_2":
["value_2",
"value_3"
]
},
{"key_1":"value_3",
...and so on...
}
]
')
I have a working powershell script with invoke-restmethod. The results are pulling back a JSON result and I need to write some if statements off of it to take action. For example, if the status = done then I need to end the application, but if the status = in progress I need to wait 20 minutes then check another field called progressURL.
How do I extract the JSON values based on the key to use in an if statement?
Here is what I have: $response = Invoke-RestMethod WORKING REQUEST THAT RETURNS JSON
Here is the json response
{
"status": done,
"Links": {
"AdditionalData": "someURL1",
"Otherurl": "someURL2",
"progressURL": "someURL3"
}
"ID": 1
}
I just need to take $response that is in JSON and be able to extract values from it to use for if statements.
You'll want to use the ConvertFrom-Json cmdlet to turn your result into a standard PowerShell object - from there it's just standard Boolean comparisons to get where you need to go.
Here's the documentation on ConvertFrom-Json to get you started:
https://technet.microsoft.com/en-us/library/hh849898.aspx
First off, I am bound to PowerShell v2 because that is what is installed by default on Windows 7. What I want to do works out of the box with PowerShell v4, and, possibly, PowerShell v3.
I want to read a JSON object from a file and use it as an object in my script. PowerShell v2 does not have the ConvertFrom-Json method, so I wrote my own implementation that I want to have the same functionality:
function ConvertFrom-Json
{
param(
[Parameter(ValueFromPipeline=$true)]
[string]$json
)
[System.Reflection.Assembly]::LoadWithPartialName("System.Web.Extensions")
$ser = New-Object System.Web.Script.Serialization.JavaScriptSerializer
$obj = $ser.DeserializeObject($json)
Write-Host $obj.GetType()
return $obj
}
I call this function like this:
$configObj = (Get-Content $configFile) -join "`n" | ConvertFrom-Json
Write-Host $configObj.GetType()
The output of this:
System.Collections.Generic.Dictionary`2[System.String,System.Object]
System.Object[]
And I am not able to interact with $configObj like an object and get it's properties. In fact, I can't figure out how to get any information out of it. When I print the object, the output looks like:
AC Version Location
--- ------- --------
True v2.0.50727 C:\Windows\assembly\GAC_MSIL\System.Web.Extensions\3.5.0.0__31bf3856ad364e35\System.Web.Extensions.dll
Key : DownloadURL
Value : https://example.com
Key : dir
Value :
When I expect it to look like:
Key Value
--- -----
DownloadURL https://example.com
dir
Is there a way to work around this and get the same object back from my ConvertFrom-Json method as exists inside my method?
Thanks in advance,
Andy
Result of every statement, with exception for assignment and increment/decrement, considered part of your function return. You does not have to use return statement to return something from function. So your function actually return two objects: loaded Assembly object from LoadWithPartialName method and dictionary from $obj variable. When you assign multiple function results to variable, PowerShell have to pack results into array. As you does not actually need Assembly object, you can cast result of LoadWithPartialName method to [void]:
[void][System.Reflection.Assembly]::LoadWithPartialName("System.Web.Extensions")
Also LoadWithPartialName method is Obsolete and you should not use it at all. For loading assembly, you can use Add-Type build-in cmdlet:
Add-Type -AssemblyName System.Web.Extensions
As you does not use named blocks in your function, all code considered to be an end block, thus you only process last pipeline input in your function. You should use process block to process every pipeline input object:
process{
$ser.DeserializeObject($json)
}
When statement return collection, than PowerShell enumerate this collection and write each individual collection's item instead of collection as single element. So if your JSON contains array at top level, you function will return array elements instead of array itself, as build-in ConvertFrom-Json does. You can use unary array operator to prevent enumeration of returned array:
,$ser.DeserializeObject($json)
The total:
function ConvertFrom-Json {
param(
[Parameter(ValueFromPipeline=$true)]
[string]$json
)
begin {
Add-Type -AssemblyName System.Web.Extensions
$ser = New-Object System.Web.Script.Serialization.JavaScriptSerializer
}
process {
,$ser.DeserializeObject($json)
}
}
I have json that looks like this:
{
"Workflow": [
{
"Parameters": {
"Project": "/Path/To/File",
"OtherParam": "True"
}
}
],
"Overrides": [
{
"Special": {
"Parameters": {
"NewParam": "NewStuffGoesHere",
"OtherParam": "False"
}
}
}
]
}
... where I want to use the Overrides.Special section to add or update fields in the workflow object. In other words, given the json above, I want to do something like this:
$config = Get-Content workflow.json | out-string | ConvertFrom-Json
$configWithOverrides = Merge-Object $config.Workflow $config.Overrides.Special
And end up with something like this:
$configWithOverrides
Parameters
----------
#{Project=/Path/To/File; NewParam=NewStuffGoesHere; OtherParam=False}
I can certainly write the Merge-Object function above to add or update values as needed based on what's in the override section, but it seems there should (could?) be a built-in or one-liner way to handle this.
I tried this:
$test = $config.Workflow + $config.Overrides.Special
...but that doesn't quite work.
$test
Parameters
----------
#{Project=/Path/To/File; OtherParam=True}
#{NewParam=NewStuffGoesHere; OtherParam=False}
This enables adding parameters:
>$test.Parameters.NewParam
NewStuffGoesHere
...but it's not so great for updating them
>$test.Parameters.OtherParam
True
False
Note - in this example, I'm choosing to handle the merge after converting the json to a psobject, but that's not a requirement.
I have a one-liner to do what you're asking for. Notice that, as far as I know, PowerShell does not deal directly with json strings. But, once converted to PowerShell objects, it's like any other object.
So, firstly, define your json file, and read it as a single string:
# Requires -Version 4
$jsonFile='c:\temp\jsonfile.json'
$jsonObj=#(gc $jsonFile -raw)|ConvertFrom-Json
Define the property upon which you want to merge the json's objects, and the 1st and 2nd objects:
$property='Parameters'
$1=$jsonObj.Workflow.$property
$2=$jsonObj.Overrides.Special.$property
Now, see the one-liner (which I've splitted in 3, for the sake of clarity):
$MergedJson=[pscustomobject]#{
$property=$2.psobject.properties|%{$11=$1}{$11|add-member $_.name $_.value -ea Ignore}{$11}
}|ConvertTo-Json
You see? $MergedJson holds the following string (using your json string):
{
"Parameters": {
"Project": "/Path/To/File",
"OtherParam": "True",
"NewParam": "NewStuffGoesHere"
}
}
Is that what you're looking for?
P.S.: if you swap the roles of $1 and $2, the common parameters' (like OtherParam) values that prevail, change.