3

Working with Graph API and Intune. I create hash table in my script and covert it to JSON which POST to GraphAPI. But in one case during conversion I lose array. Because of this GraphAPI does not want to accept JSON and returns error.

Case when conversion is correct:

$TargetGroupIDs = @('111', '222')
$AppAssignment = @{
  mobileAppAssignments = @{
  }
}

$AppAssignment.mobileAppAssignments = foreach ($GroupID in $TargetGroupIDs) {
  $Hash = [ordered]@{
    "@odata.type" = "#microsoft.graph.mobileAppAssignment"
    intent        = "Required"
    settings      = $null
    target        = @{
      "@odata.type" = "#microsoft.graph.groupAssignmentTarget"
      groupId       = "$GroupID"
    }
  }
  write-output (New-Object -Typename PSObject -Property $hash)
}

$AppAssignment | ConvertTo-Json -Depth 50

Output:

{
  "mobileAppAssignments": [
    {
      "@odata.type": "#microsoft.graph.mobileAppAssignment",
      "intent": "Required",
      "settings": null,
      "target": {
        "groupId": "111",
        "@odata.type": "#microsoft.graph.groupAssignmentTarget"
      }
    },
    {
      "@odata.type": "#microsoft.graph.mobileAppAssignment",
      "intent": "Required",
      "settings": null,
      "target": {
        "groupId": "222",
        "@odata.type": "#microsoft.graph.groupAssignmentTarget"
      }
    }
  ]
}

But when I have one element in $TargetGroupIDs conversion is not correct:

$TargetGroupIDs = @('111')
$AppAssignment = @{
  mobileAppAssignments = @{
  }
}

$AppAssignment.mobileAppAssignments = foreach ($GroupID in $TargetGroupIDs) {
  $Hash = [ordered]@{
    "@odata.type" = "#microsoft.graph.mobileAppAssignment"
    intent        = "Required"
    settings      = $null
    target        = @{
      "@odata.type" = "#microsoft.graph.groupAssignmentTarget"
      groupId       = "$GroupID"
    }
  }
  write-output (New-Object -Typename PSObject -Property $hash)
}

$AppAssignment | ConvertTo-Json -Depth 50

Output:

{
  "mobileAppAssignments": {
    "@odata.type": "#microsoft.graph.mobileAppAssignment",
    "intent": "Required",
    "settings": null,
    "target": {
      "groupId": "111",
      "@odata.type": "#microsoft.graph.groupAssignmentTarget"
    }
  }
}

Please note difference in brackets after mobileAppAssignments. In first case [], but in second case {}.

Could someone tell what I am missing in second case?

2
  • 6
    Surround your foreach loop with the array subexpression operator: $AppAssignment.mobileAppAssignments = @(foreach ($GroupID in $TargetGroupIDs) { ... }) Commented May 23, 2022 at 14:39
  • 1
    Alternatively replace foreach by the .ForEach intrinsic method, which always outputs an array-like collection: $AppAssignment.mobileAppAssignments = $TargetGroupIDs.ForEach{ and replace $GroupID by $_. Commented May 23, 2022 at 15:49

1 Answer 1

3

Theo and Santiago Squarzon have provided the crucial hint in the comments, but let me spell it out:

To ensure that the output from your foreach statement is an array, enclose it in @(), the array-subexpression operator:

$AppAssignment.mobileAppAssignments = @(
  foreach ($GroupID in $TargetGroupIDs) {
    [pscustomobject] @{
      "@odata.type" = "#microsoft.graph.mobileAppAssignment"
      intent        = "Required"
      settings      = $null
      target        = @{
        "@odata.type" = "#microsoft.graph.groupAssignmentTarget"
        groupId       = "$GroupID"
      }
    }
  }
)

Also note that simplified syntax custom-object literal syntax ([pscustomobject] @{ ... }).

@(...) ensures that an array (of type [object[]]) is returned, irrespective of how many objects, if any, the foreach statement outputs.

Without @(...), the data type of the collected output depends on the number of output objects produced by a statement or command:

  • If there is no output object, the special "AutomationNull" value is returned, which signifies the absence of output; this special value behaves like an empty collection in enumeration contexts, notably the pipeline, and like $null in expression contexts - see this answer for more information; in the context at hand, it would be converted to a null JSON value.

  • If there is one output object, it is collected as-is, as itself - this is what you saw.

  • Only with two or more output objects do you get an array (of type [object[]]).

Note: The behavior above follows from the streaming behavior of the PowerShell pipeline / its success output stream: object are emitted one by one, and needing to deal with multiple objects only comes into play if there's a need to collect output objects: see this answer for more information.

Sign up to request clarification or add additional context in comments.

2 Comments

Thank for detailed response. I not only resolved my problem, but also understood root cause.
I am glad to hear it, @Vindi4i; thanks for the feedback.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.