Parsing JSON array with PowerShell - json

I'm having issues returning specific data from an API query with PowerShell. I'm making a simple API call to grab a user which works great.
$Response = Invoke-RestMethod -uri $uri -Method GET -Headers $Headers
$Response
The call returns the following.
Total Sum Users Groups
----- ----- ----- ------
1 1 #{123456789=} #{Test Users=System.Object[]; Global Users=System.Object[]...
$Response.Users returns the following
123456789
---------
#{name= Test Case; fullname=Test Tester Case; office=Area 51;....
The issue is the number "123456789" is a unique identifier that changes with every user. In order to pull the needed data from each user I need to be able to parse and grab just that number.
Here is the JSON output from $Response.Users
{
"123456789": {
"name": "Test Case",
"fullname": "Tester Test Case",
"office": "Area 51",
"country": "United States",
"groups": [
"Test Users",
"Global Users",
"Test OU"
]
}
}
Any help is greatly appreciated!
Thanks

You can access the name of the note property using the default powershell property PsObject and retrieve the name of it:
$json =
#'
{
"123456789": {
"name": "Test Case",
"fullname": "Tester Test Case",
"office": "Area 51",
"country": "United States",
"groups": [
"Test Users",
"Global Users",
"Test OU"
]
}
}
'#
$json | ConvertFrom-Json | ForEach-Object {$_.PsObject.Properties.Name}
Or in your case:
$Response.Users | ConvertFrom-Json | ForEach-Object {$_.PsObject.Properties.Name}
Should do the trick ;-)

Related

Json value retrive from powershell

I am trying to fetch some values from json array if condition meet
My Json File:
{
"ImpactMitigationTime": 638159040000000000,
"Status": "Active",
"ExternalIncidentId": null,
"RecommendedActions": null,
"PlatformInitiated": true,
"ImpactStartTime": 638114093314870000,
"SubscriptionId": "xxxx",
"LastUpdateTime": 638120832531660300,
"EventSource": "ServiceHealth",
"TrackingId": "VNY4-RC8",
"EventLevel": "Informational",
"impactType": null,
"EventType": "HealthAdvisory",
"Priority": 22,
"duration": null,
"Summary": "<p><em>You’re receiving this notification because you use Azure Active Directory (Azure AD).</em></p>",
"Header": "Your service might have been impacted by an Azure service issue",
"Impact": [
{
"ImpactedService": "Azure Active Directory",
"ImpactedRegions": [
{
"ImpactedRegion": "West US 2",
"Status": "Active"
},
{
"ImpactedRegion": "East US",
"Status": "Resolved"
}
]
},
{
"ImpactedService": "Multi-Factor Authentication",
"ImpactedRegions": [
{
"ImpactedRegion": "South Central US",
"Status": "Active"
},
{
"ImpactedRegion": "Central US",
"Status": "Resolved"
}
]
}
],
"Title": "Action required: Add your IPv6 ranges to Conditional Access policies by 31 March 2023",
"Level": "Warning",
"IsHIR": false
}
Need PS script to pull value only of Active status Impacted regions
My Current PS Script:
$varNotifications = gc "C:\Users\pocadmin\Desktop\1.json" | ConvertFrom-Json
if($varnotifications.Impact.ImpactedRegions | Where {$_.Status -eq 'Active'})
{
$varsubscriptionId = $varnotifications.subscriptionid
$varimpactedServices = $varnotifications.Impact.ImpactedService
$varimpactedRegions = $varnotifications.Impact.ImpactedRegions.ImpactedRegion
$varstatus = $varnotifications.Impact.ImpactedRegions.status
}
$varsubscriptionId
$varimpactedServices
$varimpactedRegions
$varstatus
My Output:
xxxx
Azure Active Directory
Multi-Factor Authentication
West US 2
East US
South Central US
Central US
Active
Resolved
Active
Resolved
You shouldn't try to fetch the info in various separate variables, but have the code return an array of objects with values that belong to each other.
Something like this:
$data = Get-Content -Path 'C:\Users\pocadmin\Desktop\1.json' | ConvertFrom-Json
# loop through the data and return only objects where the Status for the ImpactedRegion equals 'Active'
$result = $data.impact | ForEach-Object {
$service = $_.ImpactedService
foreach ($region in ($_.ImpactedRegions | Where-Object {$_.Status -eq 'Active'})) {
[PsCustomObject]#{
Service = $service
Region = $region.ImpactedRegion
Status = $region.Status
}
}
}
# output on screen
$result
# output to CSV file if you like
$result | Export-Csv -Path 'C:\Users\pocadmin\Desktop\1.csv' -NoTypeInformation -UseCulture
On console screen this outputs
Service Region Status
------- ------ ------
Azure Active Directory West US 2 Active
Multi-Factor Authentication South Central US Active
The -UseCulture switch lets PowerShell create a CSV file where the delimiter character is whatever is defined in your systems ListSeparator. By doing this, you can simply double-click the file to open it in Excel on machines that have the same regional settings
Your method only gets an array of the form #(), you need to get the value from it.
$varnotifications.Impact.ImpactedRegions |Where-Object {$_.status -eq "active"} |Select-Object -Expand ImpactedRegion

Convert Azure DevOps rest api test plans for all projects to csv file using powershell

I am trying to generate report for all the test plans available in azure DevOps organization. here is my script which generates list of projects and the iterate through every project to find test plans available in it. I want all this data to be saved in csv file. I am able to get json file. Is there any way I can get this data saved in csv file with every project iterate for test plan ?
$connectionToken = ""
$BaseUrl = "https://dev.azure.com/{organization_name}/_apis/projects?
api-versions=5.0"
$base64AuthInfo=
[System.Convert]::ToBase64String([System.Text.Encoding]
'::ASCII.GetBytes(":$($connectionToken)"))
$ProjectInfo = Invoke-RestMethod -Uri $BaseUrl -Headers
#{authorization = "Basic $base64AuthInfo"} -
Method Get
$ProjectDetails = $ProjectInfo | ConvertTo-Json -Depth 100
$Projectname = $ProjectInfo.value.name
ForEach ($project in $Projectname){
$TestPlanApi = "https://dev.azure.com/{org}/$project/_apis/test/plans?
api-version=5.0"
$TestplanInfo = Invoke-RestMethod -Uri $TestPlanApi -Headers
#{authorization = "Basic $base64AuthInfo"} -Method Get
if (-NOT ($TestplanInfo.count -eq 0)){
$info = $TestplanInfo | ConvertTo-Json -Depth 100
$info
}
}
This gives me following json file , I want to convert in to csv. Due to every project test plans value starts the Json result with value I am not able to expand it and save to csv
{
"value": [
{
"id": 134,
"name": "sprint1",
"url": "https://dev.azure.com/fabrikam/fabrikam-fiber-
tfvc/_apis/test/Plans/1",
"project": {
"id": "eb6e4656-77fc-42a1-9181-4c6d8e9da5d1",
"name": "Fabrikam-Fiber-TFVC",
"url":
"https://dev.azure.com/fabrikam/_apis/projects/Fabrikam-
Fiber-
TFVC"
},
"area": {
"id": "343",
"name": "Fabrikam-Fiber-TFVC"
},
"iteration": "Fabrikam-Fiber-TFVC\\Release 1\\Sprint 1",
"state": "Active",
"rootSuite": {
"id": "1"
},
"clientUrl": "mtms://fabrikam.visualstudio.com:443/DefaultCollection/p:Fabrikam-
Fiber-TFVC/Testing/testplan/connect?id=1"
}
],
"count": 1
}
{
"value": [
{
"id": 567,
"name": "sprint1",
"url": "https://dev.azure.com/fabrikam/fabrikam-fiber-
tfvc/_apis/test/Plans/1",
"project": {
"id": "eb6e4656-77fc-42a1-9181-4c6d8e9da5d1",
"name": "Fabrikam-Fiber-TFVC",
"url": "https://dev.azure.com/fabrikam/_apis/projects/Fabrikam-Fiber-
TFVC"
},
"area": {
"id": "343",
"name": "Fabrikam-Fiber-TFVC"
},
"iteration": "Fabrikam-Fiber-TFVC\\Release 1\\Sprint 1",
"state": "Active",
"rootSuite": {
"id": "1"
},
"clientUrl":"mtms://fabrikam.visualstudio.com:443/DefaultCollection/p:Fabrikam-
Fiber-TFVC/Testing/testplan/connect?id=1"
},
{
"id": 678,
"name": "sprint1",
"url": "https://dev.azure.com/fabrikam/fabrikam-fiber-
tfvc/_apis/test/Plans/1",
"project": {
"id": "eb6e4656-77fc-42a1-9181-4c6d8e9da5d1",
"name": "Fabrikam-Fiber-TFVC",
"url": "https://dev.azure.com/fabrikam/_apis/projects/Fabrikam-Fiber-
TFVC"
},
"area": {
"id": "343",
"name": "Fabrikam-Fiber-TFVC"
},
"iteration": "Fabrikam-Fiber-TFVC\\Release 1\\Sprint 1",
"state": "Active",
"rootSuite": {
"id": "1"
},
"clientUrl":
"mtms://fabrikam.visualstudio.com:443/DefaultCollection/p:Fabrikam-
Fiber-TFVC/Testing/testplan/connect?id=1"
}
],
"count": 2
}
These are the values for different projects test runs, some projects have count = 1 then it shows one id , some projects has count =3 then it shows all the 3 ids and so on
I want this json file in csv file with columns -
id , name , url , project.name , project.id , project.url ,area.id , area.name , iteration , owner , revision , state , rootsuite.id , clienturl
How can I expand all the values in csv file ? I tried
Select-object Expand-property value but its fails to to expand all the values in json data
As a workaround, we can save the response body and then convert the json file to csv file.
We cannot save all info in one csv file, the latest info will overwrite the old data, so we need to save the test plan info in different csv files.
Sample:
$connectionToken = "{PAT}"
$BaseUrl = "https://dev.azure.com/{Org}/_apis/projects?api-version=6.1-preview.4"
$base64AuthInfo= [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes(":$($connectionToken)"))
$ProjectInfo = Invoke-RestMethod -Uri $BaseUrl -Headers #{authorization = "Basic $base64AuthInfo"} -Method Get
$ProjectDetails = $ProjectInfo | ConvertTo-Json -Depth 100
$Projectname = $ProjectInfo.value.name
ForEach ($project in $Projectname){
$TestPlanApi = "https://dev.azure.com/{Org}/$project/_apis/test/plans?api-version=5.0"
$TestplanInfo = Invoke-RestMethod -Uri $TestPlanApi -Headers #{authorization = "Basic $base64AuthInfo"} -Method Get
if (-NOT ($TestplanInfo.count -eq 0)){
#Save the test plan info to json file
$info = $TestplanInfo | ConvertTo-Json -Depth 100 | out-file E:\test\$project.json
#convert the json file to csv file
Get-Content -Path E:\test\$project.json | ConvertFrom-Json | Select-Object -expand value | ConvertTo-Csv -NoTypeInformation | Set-Content E:\test\$project.csv
#delete json file
Remove-Item E:\test\$project.json
}
}
Result:

Powershell - Update .JSON

I need some pointers on how to update values in my .json file. Here is a sample fruits.json that I have:
[
{
"ExpireDate": "",
"Origin": "",
"FruitName": "Apple"
},
{
"Expiredate": "",
"Origin": "",
"FruitName": "Orange"
}
]
I have another .JSON file call fruitdetail.json serving as an input file. I need to update the above fruits.json with the info from fruitdetail.json. Basically, lookup the "Fruit Name" from fruitdetail.json and update both "Origin" and "Expire date" keys in fruits.json. Below is what I am starting out with but it doesn't look right.
$fruits = (Get-Content -Path "C:\temp\fruits.json" | ConvertFrom-Json)
$fruitDetail = (Get-Content -Path "C:\temp\fruitdetail.json" | ConvertFrom-Json)
$json = foreach ($fruit in $fruits) {
If ($fruit.FruitName = $fruitDetail.FruitName) {
$Fruit.Origin = $FruitDetail.Origin
$Fruit.ExpireDate = $FruitDetail.ExpireDate
}
}
$json.update
Thanks in advance.
UPDATE: Updated the above code
You're trying to relate data using a common field, similar to an SQL Join. Presumably the fruit name in can be used in this case and for demonstration purposes. Not sure this is perfect, but it should demonstrate the concept.
# Establish Test Date
$JSON_Fruit =
#"
[
{
"Expire Date": "",
"Origin": "",
"Fruit Name": "Apple"
},
{
"Expire date": "",
"Origin": "",
"Fruit Name": "Orange"
}
]
"# |
ConvertFrom-Json
# Establish Test Date
$JSON_FruitDetail = #{}
(#"
[
{
"Expire Date": "10/27/2020",
"Origin": "California",
"Fruit Name": "Apple"
},
{
"Expire date": "11/10/2020",
"Origin": "Arizona",
"Fruit Name": "Orange"
}
]
"# |
ConvertFrom-Json) |
ForEach-Object{
$JSON_FruitDetail.Add( $_.'Fruit Name', $_ )
}
ForEach( $Fruit in $JSON_Fruit )
{
$Fruit."Expire Date" = $JSON_FruitDetail[$Fruit.'Fruit Name']."Expire Date"
$Fruit.Origin = $JSON_FruitDetail[$Fruit.'Fruit Name'].Origin
}
$JSON_Fruit
Note: The parenthesis are to avoid a problem in PowerShell versions prior to 7 (both Windows and Core) In PowerShell 7+ you don't need to use them. For more info see this answer and this one .
Basically you're converting the JSON into PSCustom objects via ConvertFrom-Json. Then storing one side of the data in a dictionary collection keyed on the common field, in this example the "Fruit Name". Then we're looping through the object collection using the "Fruit Name" to reference the entry in the hash table and make the assignment to the destination object's properties.
Note: I've seen a number of PowerShell functions in the community with names like Join-Object that may also be suitable for this, but I usually use techniques like above.

Only remove/Exclude an attribute from json if it exists

I have following JSON and I would like to remove streets from the JSON object if only it exists under Address which is an array. I am trying to do this in powershell. I can get my script working and remove the streets but I only want to run the exclude line of command if the address has the streets property. Is that possible?
{
"Customer": [{
"id": "123"
}],
"Nationality": [{
"name": "US",
"id": "456"
}],
"address": [{
"$type": "Home",
"name": "Houston",
"streets": [{
"name": "Union",
"postalCode": "10"
}]
},
{
"$type": "Home5",
"name": "Houston5"
},
{
"$type": "Office",
"name": "Hawai",
"streets": [{
"name": "Rock",
"postalCode": "11"
}]
}
]
}
Powershell script
$FileContent = Get-Content -Path "Test.json" -Raw | ConvertFrom-Json
#Only want to run for address objects that contains streets
$FileContent.address = $FileContent.address | Select-Object * -ExcludeProperty streets #Only would like to run if object address has streets property
$FileContent | ConvertTo-Json
Note:
This answer performs the same operation as in the question, only more succinctly, in a single pipeline.
It is benign to run Select-Object * -ExcludeProperty streets against all objects in array address, because the call is an effective no-op for those objects that already lack a streets property (though a copy of such objects is created too).
You need an assignment to modify your objects in-place before outputting them, which requires a ForEach-Object call:
Get-Content -Raw Test.json | ConvertFrom-Json |
ForEach-Object {
[array] $_.address = $_.address | select * -exclude streets; $_
}
Note how each object parsed from the JSON input is first modified via the assignment ($_.address = ...), and then passed out ($_).
A more efficient, but a little more obscure variant:
Get-Content -Raw Test.json | ConvertFrom-Json |
ForEach-Object {
$_.address.ForEach({ $_.psobject.Properties.Remove('streets') }); $_
}
With your sample JSON input, both commands output the following:
Customer Nationality address
-------- ----------- -------
{#{id=123}} {#{name=US; id=456}} {#{$type=Home; name=Houston}, #{$type=Home5; name=Houston5}, #{$type=Office; name=Hawai}}
Note how the objects in the address column no longer have a streets property.
Caveat: Note that ConvertTo-Json limits the serialization depth to 2 by default, which is sufficient in this case, but in other cases you may have to pass a -Depth argument to prevent data loss - see this post.

Adding array objects to a JSON with a property name in powershell

I've to retrieve products and it's details and save it together in a json file. Unfortunately the name and details cannot be retrieved from the same API, I gotta use 2 different API. So I run a code inside a for loop to retrieve the list of product names as well as its details as follows:
For($i=0; $i -lt $cart.total; $i++)
{
$query = 'dummy value with the cart number'
$productName = Invoke-RestMethod -Uri 'https://abc' -Body $query -Method Post
$details = Invoke-RestMethod -Uri 'https://xyz' -Body $query -Method Post
}
All this works, and below is sample data in $productname and $details.
$productName
"iPhone"
$details
"data":[
{"Moduleid": "1", "propertyName": "camera", "value": "12MP"},
{"Moduleid" : "43", "propertyName":"battery", "value": "4000MAj"}
]
However what I am trying to do is to save the $productName and $details into a json as follows:
{
"iPhone 7":[
{"Moduleid": "1", "propertyName": "camera", "value": "12MP"},
{"Moduleid" : "423", "propertyName":"battery", "value": "3000MAh"}
],
"S10+": [
{"Moduleid": "21", "propertyName": "camera", "value": "12MP"},
{"Moduleid" : "43", "propertyName":"battery", "value": "4000MAh"}
]
}
I've tried several ways but couldn't reach any conclusion. Please let me know how can these to variable datas be put into a json format as shown above.
Thank you
The question doesn't make clear what each query is returning. Generally speaking, you need to convert the return JSON from both queries into objects then merge the objects into the objects you really want. The run those objects through ConvertTo-Json which should do the trick. The source object structure is what results in the JSON text etc...
It would look something like below, but again, I have to guess at the starting point, for example I'm sure that $productName isn't a flat array, but is itself JSON.
# For test
$productName = "iPhone","S10+"
# For test
$details =
#"
{
"Data": [{
"Moduleid": "1",
"propertyName": "camera",
"value": "12MP"
},
{
"Moduleid" : "43",
"propertyName": "battery",
"value": "4000MAj"
}]
}
"# |
ConvertFrom-Json
$Results =
For( $i = 0; $i -lt $productName.count; ++$i )
{
[PSCustomObject]#{
$productName[$i] = $details.data[$i]
}
}
$Results | ConvertTo-Json
This would give back:
[
{
"iPhone": {
"Moduleid": "1",
"propertyName": "camera",
"value": "12MP"
}
},
{
"S10+": {
"Moduleid": "43",
"propertyName": "battery",
"value": "4000MAj"
}
}
]
Not quite what you're looking for, but should at least demonstrate the point.
Note: Normally I would look for a common property between the 2
returns to relate the data. Absent that I used the array index. Again
part of the reason this doesn't look right.
If you can please edit the question to include the return JSON from both queries, then we can refine the above.
I created a hashtable like:
$hashTable = [HashTable]::New(0, [StringComparer]::Ordinal)
This is so that the keys are case sensitive. Normally if you create it as #{}, it won't be case sensitive.
Then i mapped the key and value to the hash table as follows,
$hashTable.Add($productName.data.name,$details.data)
Now, I convert it to Json as I wanted it using ConvertTo-Json
$hashTable| ConvertTo-Json | Out-File $outputPath