Schedule a delayed script/function

Here is a work-around solution I put in place to delay a script/function to run (this could probably be improved, but I thought I would toss it out there).  

There are two (possibly more) scenarios where this could be needed:
1) Hybrid AD/AAD where you make an adjustment to an attribute, need to wait for the AAD Connect sync to occur before running the next step.
2) Are re-homing Azure geolocation data, and may need to wait for MS before proceeding to the next step.

To overcome this I did the following:
- Created a virtual attribute (edsva-delayedFunction)
- When needing to run a delayed script/function I populate edsva-delayed function with <time to run>;<complete function from library scripts>;<admin email that submitted the request>
    - 10/07/20 08:00:00;move-mailbox <user>;admin@acme.com
- I then have a scheduled workflow (every 15 minutes) that fires a script that will:
    - Lookup users with edsva-delayedFunction
    - See if the time is later than the current time of the script run
    - Run the function section of the line
    - Email the admin listed that the script has run (still to build out)
    - Remove the entry from edsva-delayedFunction

I had to use a scheduled workflow over the scheduled task so I could load all my function libraries required.  Below are two functions you could use to perform this.  These scripts will allow for multiple delayedFunctions to be listed for a user, and that entry is removed as it is run.  If someone has better ideas to delay "next steps" please let me know:

#### Function to Populate edvsa-delayedFunction ####
function ars-addDelayedFunction
{
    param
        (
            $objUPN,
            $function,
            $delay,
            $adminEmail
        )
    $arsServer = "<ars.acme.com>"

    $timeZoneID = "Atlantic Standard Time" #use the time zone of the server that will be running the scripts
    $timeZoneOffset = (get-timezone -Name $timeZoneID).baseUtcOffset.hours
    $now = ((get-date).ToUniversalTime()).AddHours($timeZoneOffset)

    if (!$delay) {$delay = "45"}

<#    ## Connect to ARS Server (used if testing off the ARS server)
    if ($ARS.ManagedDomains.Capacity -ne "EDMS://$arsServer/DC=ACME,DC=com")
        {
            $script:ARS = Connect-QADService -Service $arsServer -Proxy
        }
#>

    $sched = $now.AddMinutes($delay).ToString("MM/dd/yyyy HH:mm")
    [string]$attrib = $sched + ";" + $Func + ";" + $adminEmail
    $objUser = Get-QADUser $objUPN -IncludedProperties "edsva-DelayedFunction"
    $objUserAttrib = $objUser."edsva-DelayedFunction"
    if ($objUserAttrib)
        {
            $objUserAttrib = $objUserAttrib + "|" + $attrib
        }
        else
            {
                $objUserAttrib = $attrib            
            }
    set-qaduser $objUPN -ObjectAttributes @{"edsva-DelayedFunction" = $objUserAttrib} | Out-Null
}

#### Scheduled Script to run from Workflow ####

function onInit($Context)
{
    $Context.UseLibraryScript("Function Library1")
    $Context.UseLibraryScript("Function Library2")
}

function ars-delayedScripts($Request)
{
    ## Get Users with edsva-functionsToRun attribute set
    $objUsers = Get-QADUser -IncludedProperties edsva-DelayedFunction -SizeLimit 0 -LdapFilter '(edsva-DelayedFunction=*)'
    
    ## Process Users
    foreach ($objUser in $objUsers)
        {       
            Write-Host "Processing: $($objUser.DisplayName)"
            [System.Collections.ArrayList]$functionsToRemove = @()
            if ($objUser.'edsva-DelayedFunction' -like "*|*")
                {
                    [System.Collections.ArrayList]$functionList = ($objUser.'edsva-DelayedFunction').split("|") | Sort-Object
                }
                else
                    {
                        $functionList = ($objUser.'edsva-DelayedFunction')
                    }
            $functionsToRun = $functionList
            foreach ($functionToRun in $functionsToRun)
                {
                    Write-Host ""
                    Write-Host "Checking: $functionToRun"
                    $now = Get-Date
                    [datetime]$runtime = $functionToRun.Split(";")[0]
                    [string]$toRun = $functionToRun.Split(";")[1]
                    if ($now -gt $runtime)
                        {
                            Write-Host "Running: $toRun"
                            Write-Host "Removing: $functionToRun"
                            $sb = [scriptblock]::Create($toRun)
                            &$sb
                            $functionsToRemove.Add($functionToRun)
                        }
                }
            if ($functionsToRemove.count -ge 1)
                {
                    if (($functionsToRemove.Count -eq 1) -and ($functionList.count -eq 1))
                        {
                            [string]$newobjUserAttrib = $null
                        }
                        else
                            { 
                                $functionsToRemove | ForEach-Object {
                                    $functionList.Remove($_)
                                    }
                                if ($functionList.Count -gt 1)
                                    {
                                        [string]$newobjUserAttrib = $functionList -join "|"
                                    }
                                    else
                                        {
                                            [string]$newobjUserAttrib = $functionList
                                        }
                            }
                    $objUser | set-qaduser -ObjectAttributes @{"edsva-DelayedFunction" = $newobjUserAttrib} | Out-Null
                }
        }
}