Asynchronous API

Is there a simple, UD-native way for an API call to trigger an asynchronous process?

Something like
$Endpoint = New-UDEndpoint -Url “/Request” -Method Post -Endpoint {
Param( $Body )

    $Parameters = $Body | ConvertFrom-Json
    $ValidInput = Validate-InputPameters
    If ( $ValidInput )
        {
        Return 200  #  Request posted successfully
        #
        #  Complete lengthy request process here AFTER returning HTTP response
        #
        }
    Else
        {
        Return 400  #  No. Try again.
        }
    }

I believe I had tested some endpoint with Start-Job with success some time ago.
You could give that a try.

However, if using IIS, the application pool could technically recycle on you if the job is running for too long. To avoid that, or if start-job does not do the trick and you are working from a Windows environment, you can launch your job as an independant process using :

Invoke-WmiMethod -Class Win32_Process -Name Create -ArgumentList 'MyExternalProcess.exe'

I’m doing something like this myself. I have a lengthy user provisioning procedure which would take about two minutes to run. This obviously would not be acceptable for a REST request.

My approach is to call my Create-User function from the UD endpoint. The Create-User function is a master script which dot sources then calls a number of standalone functions.

These functions operate in separate runspaces after the initial Create-User has terminated. The response takes about 4 seconds but the jobs run in the background for about two minutes and log their output to a unified log file.

Function Create-User{
$kFunctions = 'C:\onboard-term-api\Functions'
. "$kFunctions\CreateHomeDir.ps1"
. "$kFunctions\CreateMailbox.ps1"
. "$kFunctions\CreateSlackAcc.ps1"

Invoke-CreateHomeDir -Username $username -DomainController $DomainController -FunctionsDir $kFunctions 

# Invoke-CreateMailbox

# Invoke-CreateSlackAcc 

# Invoke-LotsMoreStuff


Return $UserCreationDetails | ConvertTo-Json
}

An example of an async script to create a home directory in a separate runspace.

It can be hard to troubleshoot a failing script running in a separate workspace so that’s why I added the DebugMode switch. However, if this is set then the calling script will wait on the runspace to complete so only call this when you need it.

Function Invoke-HomeDirectory{
    [CmdletBinding()]
        param(
             [String]
            $Fileserver,
            [String]
            $Username,
            [String]
            $DomainController,
            [Switch]
            $DebugMode 
        )

# Params that will be used in the runspace to call New-Homedirectory
$ParamListHome = @{
    Username         = $username
    Region           =  $fileserver
    DomainController = $DomainController
    Functions        = $FunctionsDir
    } 


 $scriptblockHome = {
    Param	(
        [String] $Username, 
        [String] $Region,
        [String] $DomainController,
        [String] $Functions
    )


    Import-Module "$Functions\Modules\homeDirectory" -Verbose

    Try {
        Logwrite -Username $Username -Logstring "Running: New-HomeDirectory command from Invoke-HomeDirectory initiated runspace" 
        New-HomeDirectory -Username $Username -Region $region -DomainController $DomainController -Verbose
        }
    Catch {
        $ErrorMessage = $_.Exception.Message
        $FailedItem = $_.Exception.ItemName
        return "$FailedItem - the error message was $ErrorMessage"
        Break
        }
}

$runspaceHome = [Powershell]::Create()
$null = $runspaceHome.AddScript($scriptblockHome)
$null = $runspaceHome.AddParameters($ParamListHome)
#$runspaceHome.Runspace
#$runspaceHome.commands.commands
#$runspaceHome.commands.commands.parameters
$asyncObjectHome = $runspaceHome.BeginInvoke() # Fires off in background

If($DebugMode){
# Check status of background job


$dataHome = $runspaceHome.EndInvoke($asyncObjectHome)
LogWrite -Username $username -Logstring $dataHome
}

}

Hope this is what you are after.

Michael

Michael,

Thank you for your response and examples. But I am somewhat familiar with runspaces. (See the chapter on runspaces written by me in the PowerShell Conference Book, Vol 1.) :slight_smile:

I was hoping UD had a simple built-in solution. But it is helpful to understand that runspaces created in an endpoint can outlive the endpoint. Though it could get messy if they sometimes hang. I supposed in that case I would have to double up, and have a management runspace that creates and cleans up after the worker runspaces, handling time outs, etc.

Thanks,
Tim Curwick

1 Like

:laughing: That’s hilarious.

Maybe at some stage someone might find the example useful anyway.

But you make a good point it would be better if this didn’t require so much heavy lifting.

You got me interested in that book now too