0 votes

Hi,

You have previously advised that scheduled tasks run on the instance that they were first created on, and only if that instance is unavailable will it promote another one.

Two/three questions:-

We are looking at implementing a dedicated instance to run background scheduled tasks (especially as you know we have a growing number of custom scripted jobs). Is there any way to 're-assign' existing jobs to this new serveras their primary instance (other than completely deleting them and re-creating that we'd rather not, if possible)?

For non-script jobs where we do not have the opportunity to manually point to alternative instances in the script file, is there anyway to get them to offload to a specific instance (this is less important - generally it's anything that we've scripted ourselves that we'd like to be able to point away from the local instance).

And, out of interest, if, for example, we turn off the WAN between our Live and DR sites (which we do for DR scenario testing) and a scheduled task runs, would it run on both the Live and DR servers (our 2 current instances)?

by (1.6k points)
0

Sorry - one more, different technical subject, but same realm.

We've been tweaking the IIS settings to maximise UI performance (not that we're having any material issues, just trying to make it as smooth as possible).

As part of this we tried moving our two main portals (User Self Service and IT Operations) into separate AppPools, but found that if a user had both open that there were causing constant logouts i.e. doing anything in one caused the other to logout.

Generally each would be a separate tab in the same browser window, and we don't currently enable Kerberos auth (and we when do it will be for the SelfService portal only).

Is this expected behaviour?

1 Answer

0 votes
by (216k points)

Update 2015

For information on how to change the Adaxes service a scheduled task is run on, have a look at the following help article: https://www.adaxes.com/help/BindScheduledTaskToService.

Original

Hello,

Scheduled Task question
By default, a Scheduled Task is owned and executed on the instance that it was created on. So, if you want a specific Scheduled Task to be executed by a specific Adaxes instance in a configuration set, you need to delete it and recreate on the Adaxes service you need. However, keep in mind that if that Adaxes service goes down for a long time for some reason, another Adaxes service in the configuration set will take ownership of that Scheduled Task. Currently, there is no functionality in Adaxes to change this behavior.

As to breaking the connection between services in a configuration set, keep in mind that another Adaxes service will not take ownership of a Scheduled Task right at the time when the connection breaks. Usually, it takes about two hours before another Adaxes service takes ownership of a Scheduled Task of another service.

Web Interface issue
Actually, this is how the ASP.NET Forms Authentication works. We've assigned our Web Interface programmers the task to investigate whether it is possible to tweak the mechanism and allow being logged in to two Web Interface types simultaneously. If they find a way how to do this, we'll fix this behavior in one of our nearest releases.

0

Many thanks.

So, would it be valid to say that, if we added a third instance and scheduled all our jobs to run at, let's say 5pm, then turned off the existing instances between 4pm and 8pm that the new instance would take ownership of all the jobs - and this would be a permanent ownership unless\until we experienced a similar scenario in reverse?

If so, that sounds like a plan to me!

Rgds

0

That should work.

Don't forget to make sure that the whole of the replication has managed to successfully replicate to the new Adaxes service.

0

Thanks for this - we're re-architecting the server layout this weekend.

Couple of associated questions\seeking advice on how to manage workloads.

Would there be any performance advantage in offloading scripted custom commands called from the web UI server (or to be more precise, the Adaxes service that it normally uses) to a different Adaxes instance?

Example - we have some heavy data searches\filters\report generations that users can call from the UI, so would there be any benefit in redirecting that to run on a different server, or is the local .Net AppPool that calls the PowerShell code still going to be the limiting factor? We're thinking maybe we can re-use some large multi-core servers to act as offload servers, for specific read-only reports, to keep the primary servers snappy 100% of the time.

If we could do this, from what I can gather the script $context method always runs on the Adaxes instance that initiated it. If that is right, is there any way to still use the $context variable in the scripts while also being able to define a specific Adaxes instance (as it's quicker to use!)?


PS I know I've got a typo in the last line, and it should be $userHandle.Get :oops:

On a slightly different subject, in all my (bad) scripting I quite regularily exceed the number of allowed open connections - see below. I can clear these by restarting the Adaxes service, but was trying to see if there was a command I can issue to expunge any 'orphaned' connections?


Best Regards

0

FYI update and follow up.

We've re-coded one of the reports we had that was maxing out a core in the Adaxes server when called from the user portal for about a minute (i.e. a good test case, not too short and not too long).

We moved all (other than the original 'target user' ID) lookups from $context calls to a specified iADM instance, and i'd say >90% of the CPU use was on the 'offloaded' server - a v.good result.

PS I'm already planning to write a custom 'randomised and tolerant Adaxes instance connection selector' function to add to the start of scripts etc so that we can build a really fault tolerant\performance balanced Application->'Storage' layer. I've saidit before, and I'll say it again, you've got a fantastic product that's easy to pick and and brilliantly flexible once you start understanding what you can do!

One slight confusion is that the report actually takes about 3 minutes to run now though.

This may be hardware related, but seems strange as the 'web server' is a 4 core VM instance with virtual 2.4GHz cores, while the 'grunt server' is dedicated hardware with 16 (2 * 8) cores at 3.0GHz. I'll speak with the hardware specialists on Monday to see if it may be related to some sort of cache size\micro-architecture difference, but so far as you're aware switching to the different 'API' call's shouldn't make a material difference?

Note: Code change would be something like:-

$subordinateDNs = $userHandle.GetEx("directReports") | sort-object

from this:-

$subordinateDNs = $Context.TargetObject.GetEx("directReports") | sort-object

Finally ( :o ) is there anyway of 'isolating' the script code so that we can present the user a log message along the lines of "Report being run, check your inbox in 5 minutes" - as currently we'll only get the log messages when the entire script has run. Best way I can think of is for a 'shell' custom command to call a seperate PS script, but I'm slightly concerned for the security implications of calling external scripts with potentially privileged accounts etc.

0

Hello,

As to calling a specific Adaxes service for executing a PowerShell script and connecting to a remote Adaxes service inside the Run a program or PowerShell script action, this may be not a good idea at all. Passing Adm objects (such as the script execution context) to a remote Adaxes service can get you into trouble. The thing is that such objects can be pipelined and non-pipelined. As soon as you are dealing with such objects on a single service, the objects are non-pipelined, but if you pass the objects to another Adaxes service, they can be passed as pipelined objects only. Dealing with pipelined objects in PowerShell is very tricky, and you should know how to handle such objects really good. Also, calls on pipelined objects are much slower because you need to pass additional data together with such objects, which will probably reduce to nothing any gain that you may get from invoking a remote Adaxes service.

So, what we recommend is that when invoking a remote Adaxes service directly in your script you avoid using the $Context variable and do everything that relates to invoking the execution script context on the side of the service where the main operation is performed, and pass only simple objects like strings or integers to the remote services.

Also, we really doubt that it would give any considerable performance gain. The thing is that whenever Adaxes invokes a remote call in a PowerShell script, though the operation may be performed on the remote computer, the thread created by the script execution remains locked anyway until the operation is complete. That is, in any case, the Adaxes service that invokes a remote call will wait until the remote service completes an action.

As a general note, generating a report for 3-5 minutes is really very long. We suggest that you revert to the default script behavior (generating the report on the Adaxes service where the main operation is performed) and review your script for generating the report to make it work faster. We understand that there can be much data to include in the report, but 3 to 5 minutes is really too much. If you want, you can post the script here or send it to adaxes@softerra.com so we can review it for you and identify what can be improved.

As to the other questions:

On a slightly different subject, in all my (bad) scripting I quite regularily exceed the number of allowed open connections - see below.

This is a known issue. The thing is that PowerShell does not handle ADSI objects that can be enumerated in a foreach loop correctly. Because of this, whenever you use ADSI objects in a foreach loop or pass such objects in remote calls, PowerShell fails to free up resources/threads after using the objects. This results in exhausting the number of allowed open connections if you use such objects intensively.

We can suggest that you review your script to limit enumerations in foreach loops or not to pass objects supporting enumeration in foreach loops in remote calls.

Soon we are going to release a minor update with buxfixes and enhancements to Adaxes 2013.1, in which this issue will be addressed.

Finally ( :o ) is there anyway of 'isolating' the script code so that we can present the user a log message along the lines of "Report being run, check your inbox in 5 minutes"

The best choice here would probably be that you use two subsequent Run a program or PowerShell script actions running one after the other. For example, the sequence of actions can be as follows:

The first Run a program or PowerShell script action will be used simply to inform the user that the report is being generated. It can consist of one line only:

$Context.LogMessage("The report is being generated, check your Inbox in 5 minutes", "Information")


The second Run a program or PowerShell script action will contain the actual script for generating the report and sending it to the user's email. This second Run a program or PowerShell script action should be executed asynchronously. In this case, Adaxes will not wait for the script to complete, that is, Adaxes will show the log record generated by the first action at once. The report will be generated on the background.

The only issue with such an approach is that the user will not be able to see the warnings/errors if something goes wrong. The error/warning messages will be stored in the Execution Log of the operation, however they will not be shown to the user in the Web Interface.

0

Thanks as always for the detailed reply - hopefully also good information for others to search on! I will ponder over the script offloading 'rules'!

In terms of our reports that take a while to run. An example would be a report that users can run against their own record that lists all of their direct reports, and any managed objects - simple so far.....

...then it creates a custom formatted HTML email, with each object listed, with a direct URL link from each object so that the user can click on it to take them directly to the record....

...and (from testing, this is the bit that adds ~80% of the latency) a section which lists the review status of the object - with a direct link to the approval ticket.

FYI Here's a 'heavily obscured' example


Taking my account as an example - I have around 75 line report objects:-

If none are under review (so no approval ticket lookups are performed) this report executes in about 6 seconds.
If all are under review, so 75 pending approval ticket lookups, against around 1000 active ones on average, the report takes about a minute.

Offloading the report (in effect what you noted in your reply I think - using a single $context call to retrive the current user, then passing all of the other calls to a different instance to run) was adding about 90 seconds to this (but shifting 90% of the CPU hit onto the different instance.

However, we also tried 'looping back' - so using the 'external instance' code but connecting back to itself to perform it. This only added about 15 seconds, rather than 90.

From this we *think* about 25% overhead is added by creating the additional network stack calls. The rest seems to be related to the specific 'secondary' server, and may be something to do with the specific CPU power saving mode it has enabled, which meant that it takes time for the CPU cores to fully 'power up'.

If you don't mind I will send you one of the scripts - if you can point out any improvements I'd be interested to be able to note these down for the future. But I think the performance hits we are seeing - as you'll probably agree from the above - are because this report contains anything upto 37,500 (assuming 50% hit rate) approval databse searches, so not a product issue as far as I'm concerned!

Also - it's the nature of the beast that, the time most users are most likely to be running these reports are during 'review season' - so all at the same time, and all when there are lots of pending to approvals to go through. So a heavy impact on the primary instance of Adaxes hosting them! In this specific use case we're thinking moving these off the 'normal' host and pointing them to the 16 core server, which from our testing is a little slower (for whatever reason) but can handle 12-15 simultaneous reports being run with minimal impact on the primary web UI\Adaxes instance server.

Hopefully that all makes sense! Bottom line, we seem to have multiple options based on the flexible architecture Adaxes supports, and it's just a question of choosing the best solution.

Note - it would be nice to be able to consolidate all the logs though! :)

Rgds

0

Hello,

Our script guys have studied your script and have found several places where performance can be improved.

First of all, in your script you find the subordinates / managed objects in a search, and then bind to each subordinate / managed object and get the properties of each object. This is not optimal since each such action is a separate call. Instead, you can fetch all the required properties within the search and have only one call instead of 7-9 calls. What is more contributing to the script execution time is that you perform all the actions on a remote service. Each such operation will be a pipelined ('slow') call if you perform it on a remote Adaxes service.

So, what we suggest is to perform the search on the local service (in order to avoid pipelined calls) and fetch all the properties by the search. This is going to be extremely fast and almost no load on the service. Here's an example on how to do this for managed objects:

$searcher = New-Object "Softerra.Adaxes.Adsi.Search.DirectorySearcher" $NULL, $False
$searcher.SearchParameters.BaseObjectPath = $Context.TargetObject.AdsPath
$searcher.SearchParameters.PageSize = 500
$searcher.SearchParameters.SearchScope = "ADS_SCOPE_BASE"
$searcher.SearchParameters.Filter = "(ObjectClass=*)"
$searcher.SearchParameters.AttributeScopeQuery = "managedObjects"
$searcher.SetPropertiesToLoad(@("name","objectGUID","sAMAccountType"))

$searcherResult = $searcher.ExecuteSearch()
$result = $searcherResult.FetchAll()
$searcherResult.Dispose()

if ($result.Count -ne 0)
{
    $htmlbody += "<table border='0' width='850' cellpadding='0'><font color=#FFFFFF face='Segoe UI, Arial' size=2>
              <tr><td width='500' bgcolor=#2A6AA6><h3>Active Directory Non-User Objects</h3></td><td width='350' bgcolor=#2A6AA6></td></tr>
              <tr><td width='500' bgcolor=#5E84B8><h4>Object Name (Object Type)</h4></td><td width='350' align=center bgcolor=#5E84B8><h4>Review Due Date \ Current Status</h4></td></tr>
              </font>
              <font face='Segoe UI, Arial' size=2>"

    foreach ($managedObject in $result)
    {
        $objType = $managedObject.Properties["sAMAccountType"].Value
        if ($objType -eq "268435457")
        {
            $objTypeName = "Distribution List"
        }
        else
        {
            $objTypeName = "Security Group"
        }

        $entryname = $managedObject.Properties["name"].Value
        $entryGUIDByte = $managedObject.Properties["objectGUID"].Value
        $entryGUID = New-Object "System.Guid" (,$entryGUIDByte)
        $entryGUID = $entryGUID.ToString("D")

        $htmlbody += "<tr><td width='500'><b>$entryname</b><i> (<a href='$urlprefix $entryGUID'>$objTypeName</a>)</i></td><td width='350' bgcolor=#FFFFFF><i></i></td></tr>"
    }

    $htmlbody += "</font>
                  </table>"
}

Here we have the following line:
$searcher.SetPropertiesToLoad(@("name","objectGUID","sAMAccountType"))
The SetPropertiesToLoad method of the IAdmDirectorySearcher interface sets, which properties should be fetched by the search.

One more issue with the script is the part where you deal with Approval Requests. First of all, you bind to each Approval Request using the Request GUID. This is a very expensive operation that requires the service to search all Approval Requests and find the Request with the necessary GUID. Moreover, you get all pending Approval Requests, and then iterate through them to find the Approval Request, where the Target Object is the subordinate, and the Request Initiator is the Scheduled Task that triggers user review. If you have, say, 20 subordinates and 500 Approval Request, you will have to bind to Approval Requests 20*500 = 1000 times and get the Target Object of an Approval Request 1000 times. This is very expensive.

Here, you should remember that each Approval Request is represented as an AD object in Adaxes. Each Approval Request has the adm-ApprovalState property that indicates the state of the Approval Request (Pending, Approved, Denied, Cancelled), the adm-ApprovalRequestorGuid property that indicates the GUID of the Approval Request initiator, and the adm-TargetObjectGuid property that indicates the Target Object of the Request. So, why not just search a Pending Approval Request, where the Initiator is the Scheduled Task and the Target Object is the Subordinate? It will be many times faster than iterating through all of the approval requests for each subordinate. Here's a sample script on how to deal with subordinates:

$scheduledTaskName = "Account Review - Users" # TODO: modify me

try
{
    $subordinateDNs = $Context.TargetObject.GetEx("directReports") | sort-object
}
catch
{
    $subordinateDNs = $NULL
}

if ($subordinateDNs -ne $NULL)
{
    # Get Scheduled Task 'Account Review - Users' GUID
    $scheduledTasksContainerPath = $Context.GetWellKnownContainerPath("ScheduledTasks")

    $scheduledTaskSearcher = New-Object "Softerra.Adaxes.Adsi.Search.DirectorySearcher" $NULL, $False
    $scheduledTaskSearcher.SearchParameters.BaseObjectPath = $scheduledTasksContainerPath
    $scheduledTaskSearcher.SearchParameters.PageSize = 500
    $scheduledTaskSearcher.SearchParameters.SearchScope = "ADS_SCOPE_SUBTREE"
    $scheduledTaskSearcher.SearchParameters.Filter = "(&(objectCategory=adm-ScheduledTask)(name=$scheduledTaskName))"
    $scheduledTaskSearcher.SearchParameters.ReferralChasing = "ADS_CHASE_REFERRALS_NEVER"
    $scheduledTaskSearcher.SetPropertiesToLoad(@("objectGuid"))

    $searcherResult = $scheduledTaskSearcher.ExecuteSearch()
    $result = $searcherResult.FetchAll()
    $searcherResult.Dispose()

    if ($result.Count -eq 1)
    {
        $scheduledTaskGuidInByte = $result[0].Properties["objectGUID"].Value
        $scheduledTaskGuid = [Softerra.Adaxes.Ldap.FilterBuilder]::Create("adm-ApprovalRequestorGuid", $scheduledTaskGuidInByte)
    }
    else
    {
        $Context.LogMessage("The Scheduled Task $scheduledTaskName was not found or the name of the Scheduled Task is not unique", "Warning") # TODO: Modify me
        return
    }

    foreach ($subordinateDN in $subordinateDNs)
    {
        if ($subordinateDN -like "*CN=Users,DC=root,DC=net") # This is a fudge fix to stop references to the AD root domain causing errors
        {
            $subordinateDN = "'root' DN trapped and excluded"
            $context.LogMessage($subordinateDN, "Warning")

            continue
        }

        $subordinate = $Context.BindToObjectByDN($subordinateDN)
        $reviewDate = $subordinate.Get("adm-CustomAttributeDate1").ToString("dd/MM/yyy")
        $reviewStatus = $subordinate.Get("adm-CustomAttributeText4")

        if ($reviewStatus.startsWith("Review In Progress"))
        {
            $searchFilter = "(&(objectCategory=adm-ApprovalRequest)(adm-ApprovalState=0)$scheduledTaskGuid"

            $objectGuidInByte = $subordinate.Get("objectGUID")
            $objectGuid = [Softerra.Adaxes.Ldap.FilterBuilder]::Create("adm-TargetObjectGuid", $objectGuidInByte)
            $searchFilter += "$objectGuid)"

            $containerPath = $Context.GetWellKnownContainerPath("ApprovalRequests")

            $searcher = New-Object "Softerra.Adaxes.Adsi.Search.DirectorySearcher" $NULL, $False
            $searcher.SearchParameters.BaseObjectPath = $containerPath
            $searcher.SearchParameters.PageSize = 500
            $searcher.SearchParameters.SearchScope = "ADS_SCOPE_SUBTREE"
            $searcher.SearchParameters.Filter = $searchFilter
            $searcher.SearchParameters.ReferralChasing = "ADS_CHASE_REFERRALS_NEVER"

            $searcherResult = $searcher.ExecuteSearch()
            $requests = $searcherResult.FetchAll()
            $searcherResult.Dispose()

            foreach ($requestID in $requests)
            {
                # Bind to the approval request
                $request = $Context.BindToObject($requestID.AdsPath)

                $requestorName = $request.Requestor.Get("name")

               # TODO: Some code to include the Approval Request info in the repoort
            }
        }
    }
}

In the script, $scheduledTaskName indicates the name of the 'Account Review' Scheduled Task. Change it, if necessary.

0

Many thanks - exactly the sort of explained reasoning I was looking for!

It may be useful for you to publish some of my original code on here too for anyone else reading this - a 'bad way' \ 'good way' comparison!! :oops:

0

Thanks again for this, I'm integrating into my script and annotating as I go along so I can reuse the code in similar scenarios.

Can I just confirm a couple of points?

1) When we're building up the search filter, we're 'AND'ing' to the effect "must be a pending approval initiated by 'Review - Users' and applying to current subordinate user". If so, I 'm expecting something along the lines of:-

(&(objectCategory=adm-ApprovalRequest)(adm-ApprovalState-0)(adm-Initiator=$scheduledTaskGuid)(adm-TargetObjectGuid=objectGuid))

I think I'm right in saying the filter builder commands creates the 'red' and blue' strings above as they're making a non-ACSCII comparison?

2) When we hit the Foreach loop, we're actually only expecting a single match?

3) I had a typo in the script I sent you, as we were using the GUID of the approval ticket to directly link to it (the script had been updated incorrectly and was linking to the target object GUID). I have tried to work out how to extract the GUID as a string from the interfaces you have used in your script, are one of them correct?

Many Thanks

0

Hello,

  1. Yes, the FilterBuilder class is a helper class in Adaxes that helps to build LDAP filters requiring a special format. A good example of such filters are filters that search by a certain binary property, for example, GUID.

    If you are going to build LDAP filters in your scripts, we think the following article by Microsoft will help you to better understand the LDAP filter format: Active Directory: LDAP Syntax Filters.

  2. If you mean the foreach loop that starts with foreach ($requestID in $requests), we actually wanted to make sure that we handle all the approval requests that match the search criteria. Now, after a second thought, we understand that if there are multiple Approval Requests generated by the Scheduled Task for the same user, it is probably some error / undesired behavior. We've changed the script a bit so that it handles situations when there is 1 Approval Request and when there are several Approval Requests differently. We've also left a TODO for you there so that you can decide for yourself, what the script should do if there is more than one Approval Request.

  3. The first method that you tried is missing a line. It should be something like:

     $approvalGuidInByte = $request.Get("objectGUID")
     $approvalGuid = New-Object "System.Guid" (,$approvalGuidInByte)
     $approvalGuidInByteString = $approvalGuid.ToString("D")
    

    However, it involves binding to the AD object that represents the Approval Request. As we've already mentioned, it can sometimes be an expensive operation and requires extra calls.

    The second method that you tried is a good alternative, however, you should always remember that when using the method you should fetch the required properties in the search. That is, you need to set the properties as the properties that should be loaded using the SetPropertiesToLoad method of the searcher object. Otherwise, any attempt to get the properties will fail as the properties are not fetched and thus not present in the property cache.

    We've modified the script to use the second method that you tried (as it is much faster) and have set the objectGUID property as a property that should be fetched when searching for Approval Requests.

Also, we've found one more place where we can optimize the script a bit. The thing is that in the previous version of the script the necessary Scheduled Task was specified by the Task name. With this approach, the script execution always started with searching for the DN of the Scheduled Task to be able to bind to it and get the Task GUID. However, if the DN of the Scheduled Task is specified directly in the script, we can skip searching for the Task DN and thus save one call to the AD. This will help to make the script faster a bit.

So, to summarize, the following changes have been made to the task:

  1. Introduced different behavior when there is more than one Approval Request for a certain Subordinate and left a TODO for you so that you can decide what the script will do when there is more than one Approval Request.
  2. The Approval Request GUID is now returned by the search.
  3. The Approval Request GUID returned by the search can now be converted to a string so that you can use it in a URL.
  4. The Scheduled Task is now specified not by the Task name, but by the Task Relative Distinguished Name (RDN) in order to construct the task DN and avoid searching for the Task each time the script runs.

Here's the text of the script:

$scheduledTaskRdn = "CN=Account Review - Users" # TODO: modify me

try
{
    $subordinateDNs = $Context.TargetObject.GetEx("directReports") | sort-object
}
catch
{
    $subordinateDNs = $NULL
}

if ($subordinateDNs -ne $NULL)
{
    # Get Scheduled Task 'Account Review - Users' GUID
    $scheduledTasksContainerPath = $Context.GetWellKnownContainerPath("ScheduledTasks")
    $scheduledTasksContainerPathObj = New-Object "Softerra.Adaxes.Adsi.AdsPath" $scheduledTasksContainerPath
    $scheduledTaskPath = $scheduledTasksContainerPathObj.CreateChildPath($scheduledTaskRdn)

    $scheduledTask = $Context.BindToObject($scheduledTaskPath)
    $scheduledTaskGuidInByte = $scheduledTask.Get("objectGUID")
    $scheduledTaskGuid = [Softerra.Adaxes.Ldap.FilterBuilder]::Create("adm-ApprovalRequestorGuid", $scheduledTaskGuidInByte)

    foreach ($subordinateDN in $subordinateDNs)
    {
        if ($subordinateDN -like "*CN=Users,DC=root,DC=net") # This is a fudge fix to stop references to the AD root domain causing errors
        {
            $subordinateDN = "'root' DN trapped and excluded"
            $context.LogMessage($subordinateDN, "Warning")

            continue
        }

        $subordinate = $Context.BindToObjectByDN($subordinateDN)
        $reviewDate = $subordinate.Get("adm-CustomAttributeDate1").ToString("dd/MM/yyy")
        $reviewStatus = $subordinate.Get("adm-CustomAttributeText4")

        if ($reviewStatus.startsWith("Review In Progress"))
        {
            $searchFilter = "(&(objectCategory=adm-ApprovalRequest)(adm-ApprovalState=0)$scheduledTaskGuid"

            $objectGuidInByte = $subordinate.Get("objectGUID")
            $objectGuid = [Softerra.Adaxes.Ldap.FilterBuilder]::Create("adm-TargetObjectGuid", $objectGuidInByte)
            $searchFilter += "$objectGuid)"

            $containerPath = $Context.GetWellKnownContainerPath("ApprovalRequests")

            $searcher = New-Object "Softerra.Adaxes.Adsi.Search.DirectorySearcher" $NULL, $False
            $searcher.SearchParameters.BaseObjectPath = $containerPath
            $searcher.SearchParameters.PageSize = 500
            $searcher.SearchParameters.SearchScope = "ADS_SCOPE_SUBTREE"
            $searcher.SearchParameters.Filter = $searchFilter
            $searcher.SearchParameters.ReferralChasing = "ADS_CHASE_REFERRALS_NEVER"
            $searcher.SetPropertiesToLoad(@("objectGUID"))

            $searcherResult = $searcher.ExecuteSearch()
            $requests = $searcherResult.FetchAll()
            $searcherResult.Dispose()

            if ($requests.Count -eq 1)
            {
                $requestGuidInByte = $requests[0].Properties["objectGUID"].Value
                $requestGuid = New-Object "System.Guid" (, $requestGuidInByte)
            }
            else
            {
                # TOOD: Some code to handle situations when there is more than one Approval Request for the current subordinate
            }
        }
    }
}

In the script, $scheduledTaskRdn specifies the Scheduled Task Distinguished Name relative to the Scheduled Tasks container. For example, if your Scheduled Task is called Account Review - Users, and it is located directly in the Scheduled Tasks container, you should specify CN=Account Review - Users.

0

By the way, we've decided to include some of the features that you requested in our next release. The following features requested by you will be available starting from Adaxes 2013.2:

  1. If several Adaxes services share common configuration, you will be able to explicitly specify the service that runs a Scheduled Task.
  2. The Scheduled Tasks that are currently running will be highlighted in the Console Tree pane of Adaxes Administration Console.
  3. If you select a Scheduled Task that is currently running, you will be able to see for how long it has been running in the Result Pane (located on the right).
0

Thanks very much, and the 2013.2 scheduled task management features sound perfect!

One thing that did occur to me re: the script - and this is a generic statement for anyone who may use it as a basis for their own custom actions, and also ties into the approval queue filters you're going to add in the web UI in a later release.

Currently we use the clickable link to point directly to the (single) approval ticket that applies to the review for the object. However, it could easily be adjusted so that the script (for example) locates all requests for users to be added to a group (so the ForEach loop etc would be expected to pull back multiple entries).

Therefore, if your new web UI supports the queue filter settings being passed as a URL variable, it should be possible to construct a link that when selected drops them directly into their approval queue and pre-filters the page so that they are only looking at the approval requests that apply to that object etc.

0

Hello,

In our new Web Interface it will be possible to filter objects, and we'll think on a way how to pass filters in URLs. It should be quite easy to implement. Thanks for the suggestion!

0

Hello,

Today we released Adaxes 2013.2. Starting from this version, you can:

  1. Explicitly specify an Adaxes service that runs a Scheduled Task in a configuration set and disallow reassigning it to another service;
  2. See, which Scheduled Tasks are currently running (they are now displayed in bold in the Console Tree of the Administration Console);
  3. See, how long a Scheduled Task is running and stop it, if necessary.

You can download the new version here.

Upgrade Instructions.

For a complete list of new features and improvements, see What's New.

Related questions

0 votes
1 answer

Hi, I am trying to find a way to have a visual overview of all enabled tasks and when they are running. Example of another app which support this: Is this maybe planned in future?

asked Oct 4, 2023 by wintec01 (1.1k points)
0 votes
1 answer

When I enable a scheduled task, instead of running at the scheduled time they all run imeadiately. This is not good behavior as changes are written in a way to reflect the ... is being enabled. I am hoping there is a powershell command to stop this behavoir.

asked Jul 10, 2023 by mightycabal (1.0k points)
0 votes
0 answers

I have created 2 different scheduled tasks. One with PowerShell and one that is a if in group set an attribute. Both of these scheduled tasks are just looping. The PowerShell ... ) and then recreated. I have added send an email and I get emails each loop.

asked Apr 24, 2023 by william.malone (60 points)
0 votes
1 answer

I realise that this has been asked a few times but I can't seem to get to resolution having read through previous advice. We have scheduled tasks set up to automatically ... /time region settings on all servers currently using Adaxes. They're all set to UK.

asked Apr 21, 2023 by Homelander90 (330 points)
0 votes
1 answer

I have a PowerShell Script (being run in a Custom Command) that creates a Scheduled Task that runs another Custom Command but I want the resulting Scheduled ... Exclude = $False $scopeItem.SetInfo() $task.ActivityScopeItems.Add($scopeItem) $task.SetInfo() }

asked Apr 1, 2021 by Staj (350 points)
3,326 questions
3,026 answers
7,727 comments
544,678 users