API called before it completes mingles variables

Product: PowerShell Universal
Version: 3.9.3

The following Api works great as long as I only call it once and wait for it to complete before calling it again. The API registers a device to an Intune tenant using Graph API. If I do not wait and let it complete to register another device to 2 different tenants both the first and second systems are both registered to the tenant that was connected to on the first call. Any ideas how/why this would happen.
I have tried it using the integrated and PowerShell Environment. When the 2nd machine makes the call it will also cause an error on one or both devices. (write-information added for troubleshooting)

param([Parameter(Mandatory)]$AccountName, $SerialNumber, $DeviceHashData, $GroupTag )
Write-Information $DeviceHashData
write-information $AccountName
[xml]$xmlaccounts = Get-Content C:\ProgramData\UniversalAutomation\Repository\LCHFiles\AccountCreds.XML
$node = $xmlaccounts.Accounts.Account | Where-Object { $_.AccountName -eq “$AccountName”}

#Connect to graph using a cert
$ClientCertificate = Get-ChildItem -Path Cert:\LocalMachine\MY | Where-Object {$_.Subject -Match $LCHCertName}
$MsalToken = Get-MsalToken -AzureCloudInstance 1 -ClientId $node.AppId -TenantId $node.TenantID -ClientCertificate $ClientCertificate
Write-Information $node.appid
$connected = Connect-Graph -AccessToken $MsalToken.AccessToken

Construct Graph variables

$GraphVersion = "beta"
$GraphResource = "deviceManagement/importedWindowsAutopilotDeviceIdentities"
$GraphURI = "https://graph.microsoft.com/$($GraphVersion)/$($GraphResource)"

Construct hash table for new Autopilot device identity and convert to JSON

   $AutopilotDeviceIdentity = [ordered]@{
    '@odata.type' = '#microsoft.graph.importedWindowsAutopilotDeviceIdentity'
    'groupTag' = if ($GroupTag) { "$($GroupTag)" } else { "" }
    'serialNumber' = "$($SerialNumber)"
    'productKey' = if ($ProductKey) { "$($ProductKey)" } else { "" }
    'hardwareIdentifier' = "$($DeviceHashData)"
    'assignedUserPrincipalName' = if ($UserPrincipalName) { "$($UserPrincipalName)" } else { "" }
    'state' = @{
        '@odata.type' = 'microsoft.graph.importedWindowsAutopilotDeviceIdentityState'
        'deviceImportStatus' = 'pending'
        'deviceRegistrationId' = ''
        'deviceErrorCode' = 0
        'deviceErrorName' = ''
$AutopilotDeviceIdentityJSON = $AutopilotDeviceIdentity | ConvertTo-Json

#upload the device
$response = Invoke-GraphRequest -uri $GraphURI -body $AutopilotDeviceIdentityJSON -Method Post -verbose -ContentType “application/json” -Headers @{Authorization = “Bearer $($MsalToken.AccessToken)” }

#Return the imported device ID to be used to get assignment query ID
$ret = Disconnect-Graph
Write-Information “Disconnected Graph”
$ret = Invoke-PSUScript -name WriteReglog.ps1 -RegStatus $response -AccountName $AccountName -TenantId $node.TenantID -DeviceHashId $DeviceHashData

This is likely a problem with the graph module. Note that the graph module has a -ContentScope parameter when connecting.

The only values that are available are Process and CurrentUser. Either of these will cause a problem in PSU because APIs share the same process and are running as the same user.

        [Parameter(ParameterSetName = Constants.AppParameterSet)]
        [Parameter(ParameterSetName = Constants.UserParameterSet,
            Mandatory = false,
            HelpMessage = "Determines the scope of authentication context. This accepts `Process` for the current process, or `CurrentUser` for all sessions started by user.")]
        public ContextScope ContextScope { get; set; }

I would move the entire API code into a script and start a new process using the PS environment. The API code then just becomes and invocation of the script. You could then set the graph commands to use the process context scope to avoid stomping on each others’ session state.

Adam, thanks for the quick response. I will set this up and test today but I am sure that this should resolve the issue. I will let you know. One question. When you say start a new process using the PS environment. Is this a setting in PSU or using the PS Start-Process command?

I believe Adam was referring to moving the API code into a “Script” under Automation. The API should be able to check and validate variables, then invoke the job and feed it variables. I would check out the documentation below.

Jobs - PowerShell Universal


Thank you - just wanted to make sure. It looks pretty straight foreward. Just one more question and I will leave you alone LOL - when the API calls the script, the script will have to return a value to the API to be sent back to the caller. Is there still a chance that the value being returned could be overwritten by another call to the API?

Local variables won’t be overwritten. The problem is that the module is using process static values and by launching it in a separate process, this won’t happen.