powershell json object with square brackets - json

I have an import of 1+ emails that have to be added to a json object. A foreach loop checks to see if the emails are valid (code not addded to this example). When the import contains 2+ cases, the format of the json object is correct:
[
{
"Name": "John Doe",
"Email": "email#gmail.com"
},
{
"Name": "Jane Doe",
"Email": "email#live.com"
}
]
Namely, square brackets with each object in curly brackets, separated by comma.
But when the foreach loop only returns 1 valid email, the format becomes:
{
"Name": "John Doe",
"Email": "email#gmail.com"
}
This is the code I am running:
$body = foreach($row in $mailinglistimp)
{
#{
Email=$row.EPOSTADR
Name=$row.KUNDE_NM
}
}
$body_json = ConvertTo-json $body
How can I force the object to look like this with only one item in the foreach loop?
[
{
"Name": "John Doe",
"Email": "email#gmail.com"
},
]
On a related note, a similar problem was solved by using [int[]] $ids inside another json object, but I cant get a similar code to work using [string[]].

This is somewhat unlikely if you ask me, because you want to declare an array which contains a single item. PowerShell will always see this like a single item and won't add square brackets (array declaration for JSON). But I ran into the same issue when I was making a script to update changes in TOPdesk. So this might help someone.
$requestObject = #()
$action = "Buy a new laptop"
$status = "Rejected"
if($Action){
$requestObject += #{
op = "add"
path = "/progressTrail"
value = $Action
}
}
if($Status){
$requestObject += #{
op = "replace"
path = "/status"
value = $Status
}
}
if($requestObject){
if($requestObject.Count -eq 1){
$requestBody = "[$($requestObject | ConvertTo-Json)]"
}else{
$requestBody = $requestObject | ConvertTo-Json
}
}
The following output is generated:
[
{
"path": "/progressTrail",
"op": "add",
"value": "Buy a new laptop"
},
{
"path": "/status",
"op": "replace",
"value": "Rejected"
}
]
Now if you leave $status empty for instance, you will get the following:
[{
"path": "/progressTrail",
"op": "add",
"value": "Buy a new laptop"
}]
Note that I force the square brackets, when there is only one item in my array ($requestObject.Count -eq 1). This is not very nice imo, but it does the trick.

Related

Adding square brackets in json after split and convert from CSV in PowerShell

I have a scenario where I have to split a csv in certain number of rows, and for each batch there should be json file generated through PowerShell Script.
Here is what I am doing as of now:
$csv = "C:\Desktop\report.csv"
[int]$totalRows = 0
$reader = New-Object IO.StreamReader $csv
while($reader.ReadLine() -ne $null) { $totalRows++ }
$reader.Dispose()
$totalRows
$startRow = 0
$counter = 1
while($startRow -lt $totalRows)
{
Import-CSV $csv | Select-Object #{expression = { "Append this value"+ $_.Name}; label = 'NewName'}, #{expression = {$_.Account}; label = 'AccountNumber'} | Select-Object -skip $startRow -first 2 | ConvertTo-Json | Add-Content -Path "C:\Desktop\r_$($counter).json"
$startRow += 2
$counter++
}
The only problem here is that I am not able to enclose the Account number values in square bracket []:
Actual : "AccountNumber": "123"
expected : "AccountNumber": ["123"]
Also I am not sure on how to put whole json in each file under a root element through this. Also not sure if this "ConvertTo-Json" is the way to go as csv data needs to be edited, Please help.
Here is a csv for reference-
Name,Account,Role
John,123,Second
Rocky,345,Third
Tony,234,First
Rocky,345,Second
Matt,999,Second
Bernard,888,Third
Matt,999,First
Jacob,789,Second
Angela,777,Second
Jacob,789,First
Expected Output
First File:
{
"details":
[
{
"NewName": "Append this valueJohn",
"AccountNumber": ["123","333"]
},
{
"NewName": "Append this valueRocky",
"AccountNumber": ["345"]
}
]
}
Second File:
{
"details":
[
{
"NewName": "Append this valueTony",
"AccountNumber": ["234"]
},
{
"NewName": "Append this valueRocky",
"AccountNumber": ["345"]
}
]
}
So on till 6th file:
{
"details":
[
{
"NewName": "Append this valueAngela",
"AccountNumber": ["777"]
},
{
"NewName": "Append this valueJacob",
"AccountNumber": ["789"]
}
]
}
Thanks
Continuing from my comments as I can't use newlines in there:
Converting the below PowerShell object to Json (JavaScript Object Notation):
#{
details = #{
NewName = 'Append this valueJohn'
AccountNumber = 123
}, #{
NewName = 'Append this valueRocky'
AccountNumber = 345
}
} |ConvertTo-Json
Results in
{
"details": [
{
"NewName": "Append this valueJohn",
"AccountNumber": 123
},
{
"NewName": "Append this valueRocky",
"AccountNumber": 345
}
]
}
If you want to quote the values, you will need to force them to a string
(by using quotes or e.g. the [String] initializer):
#{
details = #{
NewName = 'Append this valueJohn'
AccountNumber = "123"
}, #{
NewName = 'Append this valueRocky'
AccountNumber = [String]345
}
} |ConvertTo-Json
Results:
{
"details": [
{
"NewName": "Append this valueJohn",
"AccountNumber": "123"
},
{
"NewName": "Append this valueRocky",
"AccountNumber": "345"
}
]
}
If you also want to put the single item (scalar) strings between square brackets (which represents an array), you will need to force the values to an array using the Array subexpression operator #( ) or the Comma operator ,. Note that this automatically happens when it concerns multiple values, e.g.: AccountNumber = "123", "333"
#{
details = #{
NewName = 'Append this valueJohn'
AccountNumber = #("123")
}, #{
NewName = 'Append this valueRocky'
AccountNumber = ,[String]345
}
} |ConvertTo-Json -Depth 9
(Also note the -Depth parameter)
Results:
{
"details": [
{
"NewName": "Append this valueJohn",
"AccountNumber": [
"123"
]
},
{
"NewName": "Append this valueRocky",
"AccountNumber": [
"345"
]
}
]
}
Note that the square brackets aren't on the same line as per default the ConvertTo-Json expands the output. You might consider to use the -Compress parameters but that will put everything on the same line:
{"details":[{"NewName":"Append this valueJohn","AccountNumber":["123"]},{"NewName":"Append this valueRocky","AccountNumber":["345"]}]}
In other words, even the appearance is different, it all technically equal and represents the same object.

(PowerShell) How to remove double quotes from the JSON output?

Here's an excerpt from my PowerShell script, that writes JSON data to a file:
$jsonData = #"
{
"FunctionName" :
{
"description": "",
"parameters": [],
"signature" : ""
}
}
"#
$jsonParameterData = #"
{
`t`t`t`t`t`t`t`t`t`t"description": "",
`t`t`t`t`t`t`t`t`t`t"name": ""
`t`t`t`t`t`t`t`t`t }
"#
$jsonData = $jsonData | ConvertFrom-Json
In the code above I have defined the JSON template that I will use to write to the file. I'm using jsonData to describe the function FunctionName with the keys of description, parameters, and signature.
On the other hand, I'm using jsonParameterData to describe each argument of the function FunctionName with the keys of description and name.
My PowerShell script pulls the data from other files, and writes this JSON information to a .json file.
For example, if the signature of the function I want to extract data from is Function FunctionName(Parameter1, Parameter2), in the .json file I would expect to see this:
"FunctionName": {
"description": "This function does this and that",
"parameters": [
{
"description": "This is the first parameter",
"name": "Parameter1"
},
{
"description": "This is the second parameter",
"name": "Parameter2"
}
],
"signature": "Function FunctionName(Parameter1, Parameter2)"
}
Somewhere in the code, I store the number of function arguments in the variable NumberOfArguments; the arguments of the function in ArrayOfArguments, and the argument descriptions in ArrayOfArgumentDescriptions.
The PowerShell code below is used to add jsonParameterData to jsonData:
for($i=0; $i -lt $NumberOfArguments; $i++){
$SelectedArgument = $ArrayOfArguments[$i]
$SelectedDescription = $ArrayOfArgumentDescriptions[$i]
$jsonData.AplusWait.parameters += $jsonParameterData
}
Then, I write jsonData to the file like this:
$jsonData = $jsonData | ConvertTo-Json | ForEach-Object { [System.Text.RegularExpressions.Regex]::Unescape($_) }
$jsonData.Substring(1, $jsonData.length - 2) | Add-Content -Path $OutputFile
Write-Output "`nFinished writing JSON data to file.`n"
However, the resulting output slightly does not match the intended output. The output looks like this:
"FunctionName": {
"description": "This function does this and that",
"parameters": [
"{
"description": "This is the first parameter",
"name": "Parameter1"
}",
"{
"description": "This is the second parameter",
"name": "Parameter2"
}"
],
"signature": "Function FunctionName(Parameter1, Parameter2)"
}
If you look closely, the output displays the parameters array as an array of strings (note the double quotes), when it should actually be an array of JSON objects.
How can I transform this:
"{
"description": "This is the first parameter",
"name": "Parameter1"
}"
into this:
{
"description": "This is the first parameter",
"name": "Parameter1"
}
?
Having the following JSONs:
$jsonData = #"
{
"foo": {
"des": "foo-des",
"par": [],
"sig": "foo-sig"
}
}
"#
$jsonParamData = #"
{
"des": "param-des",
"name": "param-name"
}
"#
$jsonObj = $jsonData | ConvertFrom-Json
$jsonParamObj = $jsonParamData | ConvertFrom-Json
# add param
$jsonObj.foo.par += $jsonParamObj
# Pay attention to the -Depth parameter otherwise the final json will be truncated
$jsonResult = $jsonObj | ConvertTo-Json -Depth 5
Write-Host $jsonResult
Should print:
{
"foo": {
"des": "foo-des",
"par": [
{
"des": "param-des",
"name": "param-name"
}
],
"sig": "foo-sig"
}
}
When converting objects back to json you should pay attention to the -Depth parameter since it will truncate your result.

Removing nested pscustomobject within Azure Rule JSON

Been struggling with deleting a nested object within JSON returned from an Azure Alert Rule query.
Thanks to folks here, I've learned how to add a PSCustomObject within another. In this case, (below) to add a webhook action
"actions": [
{
"$type": "Microsoft.WindowsAzure.Management.Monitoring.Alerts.Models.RuleEmailAction, Microsoft.WindowsAzure.Management.Mon.Client",
"odata.type": "Microsoft.Azure.Management.Insights.Models.RuleEmailAction",
"sendtoserviceowners": true,
"customEmails": [
"email2#domain.com",
"email1#domain.com"
]
}
]
$rule.properties.actions += [PSCustomObject]#{
'$type' = "Microsoft.WindowsAzure.Management.Monitoring.Alerts.Models.RuleWebhookAction, Microsoft.WindowsAzure.Management.Mon.Client";
'odata.type' = "Microsoft.WindowsAzure.Management.Monitoring.Alerts.Models.RuleWebhookAction";
serviceuri = "http://www.webhooktest.com"
}
Which results in
"actions": [
{
"$type": "Microsoft.WindowsAzure.Management.Monitoring.Alerts.Models.RuleWebhookAction, Microsoft.WindowsAzure.Management.Mon.Client",
"odata.type": "Microsoft.Azure.Management.Insights.Models.RuleWebhookAction",
"serviceUri": "http://www.webhooktest.com"
},
{
"$type": "Microsoft.WindowsAzure.Management.Monitoring.Alerts.Models.RuleEmailAction, Microsoft.WindowsAzure.Management.Mon.Client",
"odata.type": "Microsoft.Azure.Management.Insights.Models.RuleEmailAction",
"sendToServiceOwners": false,
"customEmails": [
"email2#domain.com",
"email1#domain.com"
]
}
]
I need to be able to remove the webhook and leave the email. If I could add it using +=, why does trying to remove it with -=
$rule.properties.actions -= [PSCustomObject]#{
'$type' = "Microsoft.WindowsAzure.Management.Monitoring.Alerts.Models.RuleWebhookAction, Microsoft.WindowsAzure.Management.Mon.Client";
'odata.type' = "Microsoft.WindowsAzure.Management.Monitoring.Alerts.Models.RuleWebhookAction";
serviceuri = "http://www.webhooktest.com"
}
result in
result in [System.Object[]] does not contain a method named 'op_Subtraction'
I've not had luck with the Remove() method either. What will work?

PowerShell retrieve MS Graph 3rd level data return non-json result

I got a PS script to retrieve an AuditLog event from MS Graph. The script code is below. It gets the event details in JSON format.
# Create Authentication Token for MS Graph
Function GetAuthToken
{
param
(
[Parameter(Mandatory=$true)]
$TenantName
)
Import-Module Azure
$clientId = "ef9bcdf0-a675-4cd5-9ec3-fa549f9ee4cf"
$redirectUri = "https://RedirectURI.com"
$resourceAppIdURI = "https://graph.microsoft.com"
$authority = "https://login.microsoftonline.com/$TenantName"
$authContext = New-Object "Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext" -ArgumentList $authority
$Credential = Import-Clixml -Path "C:\MIMA\tom_admin_cred.xml"
$AADCredential = New-Object "Microsoft.IdentityModel.Clients.ActiveDirectory.UserCredential" -ArgumentList $credential.UserName,$credential.Password
$authResult = $authContext.AcquireToken($resourceAppIdURI, $clientId,$AADCredential)
return $authResult
}
Function Get-aAuditEvent
{
param
(
[Parameter(Mandatory=$true)]
$Tenant
)
if($Version -eq $null) {$Version='Beta'}
#------Get the authorization token------#
$token = GetAuthToken -TenantName $tenant
#------Building Rest Api header with authorization token------#
$authHeader = #{
'Content-Type'='application\json'
'Authorization'=$token.CreateAuthorizationHeader()
}
$uri = "https://graph.microsoft.com/beta/auditlogs/directoryAudits/Directory_3WOOD_3967500"
# $uri = "https://graph.microsoft.com/beta/auditlogs/directoryAudits"
Try {
$results = Invoke-RestMethod -Uri $uri –Headers $authHeader –Method Get
$results |ConvertTo-Json -depth 4
}
catch{
Write-Host "Error while retrieving report!" -ForegroundColor red
$auditReports = $_.Exception.Response
}
}
Get-aAuditEvent -Tenant "contoso.onmicrosoft.com"
The result of the code is as below. Notice the content of "modifiedProperties" is not in JSON format? It seems somehow the value of this property has converted to hashtable. However, I tried to put this value into a hashtable and it couldn't parse it properly anyway. Maybe because "newValue" is too long for a hash table?
{
"#odata.context": "https://graph.microsoft.com/beta/$metadata#auditLogs/directoryAudits/$entity",
"id": "Directory_3WOOD_3967500",
"category": "Core Directory",
"correlationId": "559450b1-d1e8-4020-a420-4c3c6234ba44",
"result": "success",
"resultReason": "",
"activityDisplayName": "Update user",
"activityDateTime": "2018-10-13T14:57:33.328183Z",
"loggedByService": null,
"initiatedBy": {
"app": null,
"user": {
"id": "9327abf7-93ea-4007-a15c-9b77b5360cc9",
"displayName": null,
"userPrincipalName": "tom-admin#contoso.onmicrosoft.com",
"ipAddress": "\u003cnull\u003e"
}
},
"targetResources": [
{
"#odata.type": "#microsoft.graph.targetResourceUser",
"id": "2a58e6ca-2207-4fc0-ba5d-210cd5de25dc",
"displayName": null,
"userPrincipalName": "tom.chen#contoso.com",
"modifiedProperties": [
"#{displayName=AssignedLicense; oldValue=[]; newValue=[\"[SkuName=ENTERPRISEPACK, AccountId=cdc4b90d-7fa9-4a12-8d58-c2872266673c, SkuId=6fd2c87f-b296-42f0-b197-1e91e994b900, DisabledPlans=[]]\"]}",
"#{displayName=AssignedPlan; oldValue=[]; newValue=[{\"SubscribedPlanId\":\"f0e58183-18c1-4fa6-939b-e78d050533b6\",\"ServiceInstance\":\"To-Do/NA001\",\"CapabilityStatus\":0,\"AssignedTimestamp\":\"2018-10-13T14:
57:33.1683839Z\",\"InitialState\":null,\"Capability\":null,\"ServicePlanId\":\"c87f142c-d1e9-4363-8630-aaea9c4d9ae5\"},{\"SubscribedPlanId\":\"ea0d7e34-84a0-4329-910a-f38d7d4f2c00\",\"ServiceInstance\":\"OfficeForms/NA001\",\"CapabilityStatus\":0,\"AssignedTimestamp\":\"
2018-10-13T14:57:33.1683839Z\",\"InitialState\":null,\"Capability\":null,\"ServicePlanId\":\"2789c901-c14e-48ab-a76a-be334d9d793a\"},{\"SubscribedPlanId\":\"0defa810-1846-4ebf-8c01-4b72f9dbec2c\",\"ServiceInstance\":\"MicrosoftStream/NA001\",\"CapabilityStatus\":0,\"Assi
gnedTimestamp\":\"2018-10-13T14:57:33.1683839Z\",\"InitialState\":null,\"Capability\":null,\"ServicePlanId\":\"9e700747-8b1d-45e5-ab8d-ef187ceec156\"},{\"SubscribedPlanId\":\"e56c4814-73b8-4a12-ac13-bd2236e1c61c\",\"ServiceInstance\":\"Deskless/NA001\",\"CapabilityStatus
\":0,\"AssignedTimestamp\":\"2018-10-13T14:57:33.1683839Z\",\"InitialState\":null,\"Capability\":null,\"ServicePlanId\":\"8c7d2df8-86f0-4902-b2ed-a0458298f3b3\"},{\"SubscribedPlanId\":\"ef68b42e-5730-41b8-b119-a78dd199cd39\",\"ServiceInstance\":\"ProcessSimple/NA001\",\"
CapabilityStatus\":0,\"AssignedTimestamp\":\"2018-10-13T14:57:33.1683839Z\",\"InitialState\":null,\"Capability\":null,\"ServicePlanId\":\"76846ad7-7776-4c40-a281-a386362dd1b9\"},{\"SubscribedPlanId\":\"d69c693a-dfc8-49f8-9bd2-68b570bc3dd8\",\"ServiceInstance\":\"PowerApp
sService/NA001\",\"CapabilityStatus\":0,\"AssignedTimestamp\":\"2018-10-13T14:57:33.1683839Z\",\"InitialState\":null,\"Capability\":null,\"ServicePlanId\":\"c68f8d98-5534-41c8-bf36-22fa496fa792\"},{\"SubscribedPlanId\":\"b3e7a5a5-bfae-4ae6-887c-ce9665de0610\",\"ServiceIn
stance\":\"TeamspaceAPI/NA001\",\"CapabilityStatus\":0,\"AssignedTimestamp\":\"2018-10-13T14:57:33.1683839Z\",\"InitialState\":null,\"Capability\":null,\"ServicePlanId\":\"57ff2da0-773e-42df-b2af-ffb7a2317929\"},{\"SubscribedPlanId\":\"c9dbc746-7d1d-449f-9a2c-f80c99df11f
2\",\"ServiceInstance\":\"ProjectWorkManagement/PROD_OC_Org_Ring_010\",\"CapabilityStatus\":0,\"AssignedTimestamp\":\"2018-10-13T14:57:33.1683839Z\",\"InitialState\":null,\"Capability\":null,\"ServicePlanId\":\"b737dad2-2f6c-4c65-90e3-ca563267e8b9\"},{\"SubscribedPlanId\
":\"6aa67dd9-afd1-47c4-b81f-065ba3495692\",\"ServiceInstance\":\"Sway/NA001\",\"CapabilityStatus\":0,\"AssignedTimestamp\":\"2018-10-13T14:57:33.1683839Z\",\"InitialState\":null,\"Capability\":null,\"ServicePlanId\":\"a23b959c-7ce8-4e57-9140-b90eb88a9e97\"},{\"Subscribed
PlanId\":\"6d4d99fc-d0e1-4350-a4da-cb79cadd739e\",\"ServiceInstance\":\"YammerEnterprise/NA009\",\"CapabilityStatus\":0,\"AssignedTimestamp\":\"2018-10-13T14:57:33.1683839Z\",\"InitialState\":null,\"Capability\":null,\"ServicePlanId\":\"7547a3fe-08ee-4ccb-b430-5077c50416
53\"},{\"SubscribedPlanId\":\"ac1fca1c-7d64-476c-b1f8-1c336ccac213\",\"ServiceInstance\":\"RMSOnline/AP\",\"CapabilityStatus\":0,\"AssignedTimestamp\":\"2018-10-13T14:57:33.1683839Z\",\"InitialState\":null,\"Capability\":null,\"ServicePlanId\":\"bea4c11e-220a-4e6d-8eb8-8
ea15d019f90\"},{\"SubscribedPlanId\":\"2cc87a99-6c05-4bf2-a8a7-4a75e26a6afd\",\"ServiceInstance\":\"MicrosoftOffice/NorthAmerica\",\"CapabilityStatus\":0,\"AssignedTimestamp\":\"2018-10-13T14:57:33.1683839Z\",\"InitialState\":null,\"Capability\":null,\"ServicePlanId\":\"
43de0ff5-c92c-492b-9116-175376d08c38\"},{\"SubscribedPlanId\":\"97006162-e810-4814-98e7-3ae3745b28bc\",\"ServiceInstance\":\"MicrosoftCommunicationsOnline/Instance04-S\",\"CapabilityStatus\":0,\"AssignedTimestamp\":\"2018-10-13T14:57:33.1683839Z\",\"InitialState\":null,\
"Capability\":null,\"ServicePlanId\":\"0feaeb32-d00e-4d66-bd5a-43b5b83db82c\"},{\"SubscribedPlanId\":\"10985cf4-2206-4e47-9910-426586912b1a\",\"ServiceInstance\":\"SharePoint/SPOS0017\",\"CapabilityStatus\":0,\"AssignedTimestamp\":\"2018-10-13T14:57:33.1683839Z\",\"Initi
alState\":null,\"Capability\":null,\"ServicePlanId\":\"e95bec33-7c88-4a70-8e19-b10bd9d0c014\"},{\"SubscribedPlanId\":\"e0592405-cc57-4152-8cc0-3f8e5651e47d\",\"ServiceInstance\":\"SharePoint/SPOS0017\",\"CapabilityStatus\":0,\"AssignedTimestamp\":\"2018-10-13T14:57:33.16
83839Z\",\"InitialState\":null,\"Capability\":null,\"ServicePlanId\":\"5dbe027f-2339-4123-9542-606e4d348a72\"},{\"SubscribedPlanId\":\"552916d8-55f1-44be-a7e1-9a56b8086a9b\",\"ServiceInstance\":\"exchange/apcprd03-001-01\",\"CapabilityStatus\":0,\"AssignedTimestamp\":\"2
018-10-13T14:57:33.1683839Z\",\"InitialState\":null,\"Capability\":null,\"ServicePlanId\":\"efb87545-963c-4e0d-99df-69c6916d9eb0\"}]}",
"#{displayName=Included Updated Properties; oldValue=; newValue=\"AssignedLicense, AssignedPlan\"}",
"#{displayName=TargetId.UserType; oldValue=; newValue=\"Member\"}"
]
}
],
"additionalDetails": [
{
"key": "UserType",
"value": "Member"
}
]
}
This is very odd, as when I retrieve the same event from MS Graph Explorer, I get different result, which is all in proper JSON foramt. Below is the output from MS Graph Explorer. As you can see, the "modifiedProperties" pair is still presented in JSON format.
{
"#odata.context": "https://graph.microsoft.com/beta/$metadata#auditLogs/directoryAudits/$entity",
"id": "Directory_3WOOD_3967500",
"category": "Core Directory",
"correlationId": "559450b1-d1e8-4020-a420-4c3c6234ba44",
"result": "success",
"resultReason": "",
"activityDisplayName": "Update user",
"activityDateTime": "2018-10-13T14:57:33.328183Z",
"loggedByService": null,
"initiatedBy": {
"app": null,
"user": {
"id": "9327abf7-93ea-4007-a15c-9b77b5360cc9",
"displayName": null,
"userPrincipalName": "tom-admin#contoso.onmicrosoft.com",
"ipAddress": "<null>"
}
},
"targetResources": [
{
"#odata.type": "#microsoft.graph.targetResourceUser",
"id": "2a58e6ca-2207-4fc0-ba5d-210cd5de25dc",
"displayName": null,
"userPrincipalName": "tom.chen#contoso.com",
"modifiedProperties": [
{
"displayName": "AssignedLicense",
"oldValue": "[]",
"newValue": "[\"[SkuName=ENTERPRISEPACK, AccountId=cdc4b90d-7fa9-4a12-8d58-c2872266673c, SkuId=6fd2c87f-b296-42f0-b197-1e91e994b900, DisabledPlans=[]]\"]"
},
{
"displayName": "AssignedPlan",
"oldValue": "[]",
"newValue": "[{\"SubscribedPlanId\":\"f0e58183-18c1-4fa6-939b-e78d050533b6\",\"ServiceInstance\":\"To-Do/NA001\",\"CapabilityStatus\":0,\"AssignedTimestamp\":\"2018-10-13T14:57:33.1683839Z\",\"InitialState\":null,\"Capability\":null,\"ServicePlanId\":\"c87f142c-d1e9-4363-8630-aaea9c4d9ae5\"},{\"SubscribedPlanId\":\"ea0d7e34-84a0-4329-910a-f38d7d4f2c00\",\"ServiceInstance\":\"OfficeForms/NA001\",\"CapabilityStatus\":0,\"AssignedTimestamp\":\"2018-10-13T14:57:33.1683839Z\",\"InitialState\":null,\"Capability\":null,\"ServicePlanId\":\"2789c901-c14e-48ab-a76a-be334d9d793a\"},{\"SubscribedPlanId\":\"0defa810-1846-4ebf-8c01-4b72f9dbec2c\",\"ServiceInstance\":\"MicrosoftStream/NA001\",\"CapabilityStatus\":0,\"AssignedTimestamp\":\"2018-10-13T14:57:33.1683839Z\",\"InitialState\":null,\"Capability\":null,\"ServicePlanId\":\"9e700747-8b1d-45e5-ab8d-ef187ceec156\"},{\"SubscribedPlanId\":\"e56c4814-73b8-4a12-ac13-bd2236e1c61c\",\"ServiceInstance\":\"Deskless/NA001\",\"CapabilityStatus\":0,\"AssignedTimestamp\":\"2018-10-13T14:57:33.1683839Z\",\"InitialState\":null,\"Capability\":null,\"ServicePlanId\":\"8c7d2df8-86f0-4902-b2ed-a0458298f3b3\"},{\"SubscribedPlanId\":\"ef68b42e-5730-41b8-b119-a78dd199cd39\",\"ServiceInstance\":\"ProcessSimple/NA001\",\"CapabilityStatus\":0,\"AssignedTimestamp\":\"2018-10-13T14:57:33.1683839Z\",\"InitialState\":null,\"Capability\":null,\"ServicePlanId\":\"76846ad7-7776-4c40-a281-a386362dd1b9\"},{\"SubscribedPlanId\":\"d69c693a-dfc8-49f8-9bd2-68b570bc3dd8\",\"ServiceInstance\":\"PowerAppsService/NA001\",\"CapabilityStatus\":0,\"AssignedTimestamp\":\"2018-10-13T14:57:33.1683839Z\",\"InitialState\":null,\"Capability\":null,\"ServicePlanId\":\"c68f8d98-5534-41c8-bf36-22fa496fa792\"},{\"SubscribedPlanId\":\"b3e7a5a5-bfae-4ae6-887c-ce9665de0610\",\"ServiceInstance\":\"TeamspaceAPI/NA001\",\"CapabilityStatus\":0,\"AssignedTimestamp\":\"2018-10-13T14:57:33.1683839Z\",\"InitialState\":null,\"Capability\":null,\"ServicePlanId\":\"57ff2da0-773e-42df-b2af-ffb7a2317929\"},{\"SubscribedPlanId\":\"c9dbc746-7d1d-449f-9a2c-f80c99df11f2\",\"ServiceInstance\":\"ProjectWorkManagement/PROD_OC_Org_Ring_010\",\"CapabilityStatus\":0,\"AssignedTimestamp\":\"2018-10-13T14:57:33.1683839Z\",\"InitialState\":null,\"Capability\":null,\"ServicePlanId\":\"b737dad2-2f6c-4c65-90e3-ca563267e8b9\"},{\"SubscribedPlanId\":\"6aa67dd9-afd1-47c4-b81f-065ba3495692\",\"ServiceInstance\":\"Sway/NA001\",\"CapabilityStatus\":0,\"AssignedTimestamp\":\"2018-10-13T14:57:33.1683839Z\",\"InitialState\":null,\"Capability\":null,\"ServicePlanId\":\"a23b959c-7ce8-4e57-9140-b90eb88a9e97\"},{\"SubscribedPlanId\":\"6d4d99fc-d0e1-4350-a4da-cb79cadd739e\",\"ServiceInstance\":\"YammerEnterprise/NA009\",\"CapabilityStatus\":0,\"AssignedTimestamp\":\"2018-10-13T14:57:33.1683839Z\",\"InitialState\":null,\"Capability\":null,\"ServicePlanId\":\"7547a3fe-08ee-4ccb-b430-5077c5041653\"},{\"SubscribedPlanId\":\"ac1fca1c-7d64-476c-b1f8-1c336ccac213\",\"ServiceInstance\":\"RMSOnline/AP\",\"CapabilityStatus\":0,\"AssignedTimestamp\":\"2018-10-13T14:57:33.1683839Z\",\"InitialState\":null,\"Capability\":null,\"ServicePlanId\":\"bea4c11e-220a-4e6d-8eb8-8ea15d019f90\"},{\"SubscribedPlanId\":\"2cc87a99-6c05-4bf2-a8a7-4a75e26a6afd\",\"ServiceInstance\":\"MicrosoftOffice/NorthAmerica\",\"CapabilityStatus\":0,\"AssignedTimestamp\":\"2018-10-13T14:57:33.1683839Z\",\"InitialState\":null,\"Capability\":null,\"ServicePlanId\":\"43de0ff5-c92c-492b-9116-175376d08c38\"},{\"SubscribedPlanId\":\"97006162-e810-4814-98e7-3ae3745b28bc\",\"ServiceInstance\":\"MicrosoftCommunicationsOnline/Instance04-S\",\"CapabilityStatus\":0,\"AssignedTimestamp\":\"2018-10-13T14:57:33.1683839Z\",\"InitialState\":null,\"Capability\":null,\"ServicePlanId\":\"0feaeb32-d00e-4d66-bd5a-43b5b83db82c\"},{\"SubscribedPlanId\":\"10985cf4-2206-4e47-9910-426586912b1a\",\"ServiceInstance\":\"SharePoint/SPOS0017\",\"CapabilityStatus\":0,\"AssignedTimestamp\":\"2018-10-13T14:57:33.1683839Z\",\"InitialState\":null,\"Capability\":null,\"ServicePlanId\":\"e95bec33-7c88-4a70-8e19-b10bd9d0c014\"},{\"SubscribedPlanId\":\"e0592405-cc57-4152-8cc0-3f8e5651e47d\",\"ServiceInstance\":\"SharePoint/SPOS0017\",\"CapabilityStatus\":0,\"AssignedTimestamp\":\"2018-10-13T14:57:33.1683839Z\",\"InitialState\":null,\"Capability\":null,\"ServicePlanId\":\"5dbe027f-2339-4123-9542-606e4d348a72\"},{\"SubscribedPlanId\":\"552916d8-55f1-44be-a7e1-9a56b8086a9b\",\"ServiceInstance\":\"exchange/apcprd03-001-01\",\"CapabilityStatus\":0,\"AssignedTimestamp\":\"2018-10-13T14:57:33.1683839Z\",\"InitialState\":null,\"Capability\":null,\"ServicePlanId\":\"efb87545-963c-4e0d-99df-69c6916d9eb0\"}]"
},
{
"displayName": "Included Updated Properties",
"oldValue": null,
"newValue": "\"AssignedLicense, AssignedPlan\""
},
{
"displayName": "TargetId.UserType",
"oldValue": null,
"newValue": "\"Member\""
}
]
}
],
"additionalDetails": [
{
"key": "UserType",
"value": "Member"
}
]
}
In fact both results (via PowerShell and Microsoft Graph Explorer) seems to be identical (except some formatting differences).
In both cases a valid JSON value is returned.
Now comes the turn of oldValue and newValue property values.
According to documentation, modifiedProperty property of targetResource resource
returns the collection of name, old value and new value which represented in JSON format like this:
{
"displayName": "String",
"newValue": "String",
"oldValue": "String"
}
meaning newValue and oldValue return the converted to string values.
Example
{
"#odata.context": "https://graph.microsoft.com/beta/$metadata#auditLogs/directoryAudits/$entity",
"id": "Directory_GIRJA_107870298",
//...
"targetResources": [
{
"#odata.type": "#microsoft.graph.targetResourceGroup",
//...
"modifiedProperties": [
{
"displayName": "foo",
"oldValue": "[]",
"newValue": "[\"bar\"]"
},
//...
{
"displayName": "json_value",
"oldValue": null,
"newValue": "[{\"first_name\":\"Jon\",\"last_name\":\"Doe\"}]"
}
]
}
],
"additionalDetails": []
}
Note: pay attention to the second modifiedProperty entry newValue property, which represents JSON value and is returned as a string: {"first_name":"Jon","last_name":"Doe"}
Using the following example newValue could be converted to JSON:
$uri = "https://graph.microsoft.com/beta/auditLogs/directoryAudits/{directory-id}"
$results = Invoke-RestMethod -Uri $uri -Headers $headers -Method Get
$results.targetResources | Select -ExpandProperty modifiedProperties | Select -ExpandProperty newValue | ConvertFrom-Json
I managed to solve the problem by putting the "modifiedProperties" section into a hashtable (Didn't do this properly last time). I use a function to convert the JSON result to Hashtable. The function code is copied from here.
Function ConvertTo-Hashtable {
[CmdletBinding()]
[OutputType('hashtable')]
param (
[Parameter(ValueFromPipeline)]
$InputObject
)
process {
## Return null if the input is null. This can happen when calling the function
## recursively and a property is null
if ($null -eq $InputObject) {
return $null
}
## Check if the input is an array or collection. If so, we also need to convert
## those types into hash tables as well. This function will convert all child
## objects into hash tables (if applicable)
if ($InputObject -is [System.Collections.IEnumerable] -and $InputObject -isnot [string]) {
$collection = #(
foreach ($object in $InputObject) {
ConvertTo-Hashtable -InputObject $object
}
)
## Return the array but don't enumerate it because the object may be pretty complex
Write-Output -NoEnumerate $collection
} elseif ($InputObject -is [psobject]) { ## If the object has properties that need enumeration
## Convert it to its own hash table and return it
$hash = #{}
foreach ($property in $InputObject.PSObject.Properties) {
$hash[$property.Name] = ConvertTo-Hashtable -InputObject $property.Value
}
$hash
} else {
## If the object isn't an array, collection, or other object, it's already a hash table
## So just return it.
$InputObject
}
}
}
$hashtable1 = #{}
$hashtable2 = #{}
$hashtable1 = Get-aAuditEvent -Tenant "contoso.onmicrosoft.com"|ConvertFrom-Json|ConvertTo-HashTable
$hashtable2 = $hashtable1.targetResources.modifiedProperties
$hashtable2[0].displayName
$hashtable2[0].oldValue
$hashtable2[0].newValue
The output of the code is below. I still need to workout how to parse "newValue" properly. But at least I can show what actions have been done and what have been changed.
AssignedLicense
[]
["[SkuName=ENTERPRISEPACK, AccountId=cdc4b90d-7fa9-4a12-8d58-c2872266673c, SkuId=6fd2c87f-b296-42f0-b197-1e91e994b900, DisabledPlans=[]]"]

Cannot convert PSCustomObjects within array back to JSON correctly

I'm trying to ingest a JSON file into Powershell, append a block of JSON to an existing node (Components), then convert the PSCustomObject back to JSON and save the file. The JSON I'm playing with looks something like Figure 1.
As you see in my code, I run ConvertTo-Json to cast the data into a PSCustomObject, and I then append a new object to the Components node. If I view the object, $configFile in this case it all looks fine, but when I convert back to JSON the items in the Components node, are treated as strings and not evaluated into JSON (see last snippet). I imagine this is because ConvertTo-JSON treats arrays literally, but not 100% sure.
If someone can advise how to ensure the PSCustomObjects in the Components node get casted back to JSON properly I would be grateful, thank you.
Figure 1 - the original JSON:
{
"EngineConfiguration": {
"PollInterval": "00:00:15",
"Components": [
{
"Id": "ApplicationEventLog",
"FullName": "AWS.EC2.Windows.CloudWatch.EventLog.EventLogInputComponent,AWS.EC2.Windows.CloudWatch",
"Parameters": {
"LogName": "Application",
"Levels": "1"
}
},
{
"Id": "SystemEventLog",
"FullName": "AWS.EC2.Windows.CloudWatch.EventLog.EventLogInputComponent,AWS.EC2.Windows.CloudWatch",
"Parameters": {
"LogName": "System",
"Levels": "7"
}
}
],
"Flows": {
"Flows":
[
"(ApplicationEventLog,SystemEventLog),CloudWatchLogs"
]
}
}
}
Figure 2 - my code:
#Requires -Version 3.0
$configFile = "C:\Program Files\Amazon\EC2ConfigService\Settings\AWS.EC2.Windows.CloudWatch.json"
$configToPSObject = ConvertFrom-Json "$(Get-Content $configFile)"
$configToPSObject.EngineConfiguration.Components += New-Object -Type PSObject -Property ([ordered]#{
"Id" = "IISRequestQueueSize"
"FullName" = "AWS.EC2.Windows.CloudWatch.PerformanceCounterComponent.PerformanceCounterInputComponent,AWS.EC2.Windows.CloudWatch"
"Parameters" = [PSCustomObject]#{
"CategoryName" = "HTTP Service Request Queues"
"CounterName" = "CurrentQueueSize"
"InstanceName" = "_Total"
"MetricName" = "IISRequestQueueSize"
"Unit" = ""
"DimensionName" = ""
"DimensionValue" = ""
}
})
$configJson = ConvertTo-Json -Depth 5 $configToPSObject
Set-Content -Path $configFile -Value $configJson
Figure 3 - the JSON output:
{
"EngineConfiguration": {
"PollInterval": "00:00:15",
"Components": [
"#{Id=ApplicationEventLog; FullName=AWS.EC2.Windows.CloudWatch.EventLog.EventLogInputComponent,AWS.EC2.Windows.CloudWatch; Parameters=}",
"#{Id=SystemEventLog; FullName=AWS.EC2.Windows.CloudWatch.EventLog.EventLogInputComponent,AWS.EC2.Windows.CloudWatch; Parameters=}",
"#{Id=IISRequestQueueSize; FullName=AWS.EC2.Windows.CloudWatch.PerformanceCounterComponent.PerformanceCounterInputComponent,AWS.EC2.Windows.CloudWatch; Parameters=}"
],
"Flows": {
"Flows":
"(ApplicationEventLog,SystemEventLog),CloudWatchLogs"
}
}
}
If I increase the depth to say, 8 or beyond, the JSON comes out as follows:
{
"EngineConfiguration": {
"PollInterval": "00:00:15",
"Components": [
"#{Id=ApplicationEventLog; FullName=AWS.EC2.Windows.CloudWatch.EventLog.EventLogInputComponent,AWS.EC2.Windows.CloudWatch; Parameters=}",
"#{Id=SystemEventLog; FullName=AWS.EC2.Windows.CloudWatch.EventLog.EventLogInputComponent,AWS.EC2.Windows.CloudWatch; Parameters=}",
"Id": "IISRequestQueueSize",
"FullName": "AWS.EC2.Windows.CloudWatch.PerformanceCounterComponent.PerformanceCounterInputComponent,AWS.EC2.Windows.CloudWatch",
"Parameters": {
"CategoryName": "HTTP Service Request Queues",
"CounterName": "CurrentQueueSize",
"InstanceName": "_Total",
"MetricName": "IISRequestQueueSize",
"Unit": "",
"DimensionName": "",
"DimensionValue": ""
}
}
],
"Flows": {
"Flows": "(ApplicationEventLog,SystemEventLog),CloudWatchLogs"
}
}
}
The ConvertTo-Json cmdlet also has a Depth parameter, beyond which an object is treated with toString() instead of going deeper with recursion. So just setting that parameter to whatever max depth of objects you have should result in a correctly formed JSON.
$configJson = ConvertTo-Json $configToPSObject -Depth 8
# your JSON has depth of 5, get some extra
You have to supply the depth for the ConvertTo-Json commandlet.
Otherwise it only does the first level and leaves the subnodes as is and converts them to a string apparently.
$configJson = ConvertTo-Json $obj -Depth 3