powershellconvertto-json

Powershell - ConvertTo-Json hangs script


Evening all, I have a script block below to add a user to an application in OKTA. It runs perfectly if I define a user ($login) and run the script, however if I wrap the script block below in a ForEach loop it hangs indefinitely without proceeding past the $jsonBody = $body | ConvertTo-Json -compress -depth 10 line. I came to this conclusion by adding numbered Write-Host lines after each command. I have tried removing the -Compress and -depth switches which results in an Error 400 bad request meaning the body for Invoke-Webrequest is badly formed. Can you see any reason for this hanging?

$body = @()
$userRequest = "https://$org/api/v1/users?q=$login"
$WebResponse = Invoke-WebRequest -Headers $headers -Method Get -Uri $userRequest
$user = $webresponse | ConvertFrom-Json
$UserID = $user.id
$Body = @{
            id = $UserID
            scope = "USER"
            credentials = @{
            userName = $login
            }
         }
$jsonBody = $body | ConvertTo-Json -compress -depth 10
$UpdateRequest = "https://$org/api/v1/apps/$AppID/users"
Invoke-WebRequest -Headers $headers -Body $jsonBody -Method POST -Uri $UpdateRequest
Write-Host "$login added to $AppName successfully" -ForegroundColor Green  

ForEach loop example below with method to check where it failed:

$logins = Get-Content "C:\ScriptRepository\Users.txt"

ForEach ($login in $logins) {
    $body = @()
    Write-Host "1" -ForegroundColor Green
    $userRequest = "https://$org/api/v1/users?q=$login"
    Write-Host "2" -ForegroundColor Green
    $WebResponse = Invoke-WebRequest -Headers $headers -Method Get -Uri $userRequest
    Write-Host "3" -ForegroundColor Green
    $user = $webresponse | ConvertFrom-Json
    Write-Host "4" -ForegroundColor Green
    $UserID = $user.id
    Write-Host "5" -ForegroundColor Green
    $Body = @{
                id = $UserID
                scope = "USER"
                credentials = @{
                userName = $login
                }
             }
    Write-Host "6" -ForegroundColor Green
    $jsonBody = $body | ConvertTo-Json -Compress -Depth 10
    Write-Host "7" -ForegroundColor Green
    $UpdateRequest = "https://$org/api/v1/apps/$AppID/users"
    Write-Host "8" -ForegroundColor Green
    Invoke-WebRequest -Headers $headers -Body $jsonBody -Method POST -Uri $UpdateRequest
    Write-Host "$login added to $AppName successfully" -ForegroundColor Green          
}

Below is the content of $jsonBody if I remove -compress -depth 10 from ConvertTo-Json, I have anonymised the email:

{
    "scope":  "USER",
    "credentials":  {
                        "userName":  {
                                         "value":  "user.email@add.com",
                                         "PSPath":  "C:\\ScriptRepository\\Users.txt",
                                         "PSParentPath":  "C:\\ScriptRepository",
                                         "PSChildName":  "Users.txt",
                                         "PSDrive":  "C",
                                         "PSProvider":  "Microsoft.PowerShell.Core\\FileSystem",
                                         "ReadCount":  2
                                     }
                    },
    "id":  "00u1230u44rbpE4z70i7"
}

Solution

  • To add some background information to your own effective solution:

    This problem no longer occurs in PowerShell (Core) 7.1+, where decorated strings ([string]) and date values ([datetime]) now serialize just like undecorated ones; however, instances of all other types that have ETS properties are still serialized as custom objects, as described above - see GitHub issue #5797 for the discussion that led to this change.


    Efficient alternative to individual .ToString() calls on the lines returned by Get-Content:

    An efficient and conceptually simply solution is one of the ones Santiago Squarzon mentioned in a comment:

    # Note the -ReadCount 0
    # Each value of $login in the loop is then undecorated.
    foreach ($login in (Get-Content -ReadCount 0 "C:\ScriptRepository\Users.txt")) {
      # ...
    }