Use loops
  • 05 Sep 2023
  • 12 Minutes to read
  • Dark

Use loops

  • Dark

Article Summary

A loop is a series of continually repeated actions until a certain condition is reached. Alternatively, a loop can also repeat a series of actions on a certain data set. In this case, the action is repeated per the number of items in a dataset.

In Torq, loops are used in workflows for several use cases.

Loop in

  • Loop over a dataset and perform an action on each dataset item.
  • Loop over a dataset until an object matches certain criteria.

Loop range

  • Loop times (or less) until a certain action takes place.
  • Loop times to retrieve multiple pages of results (pagination).

Key concepts

Understanding the terms and concepts associated with using loops in Torq is important.

Loop operator

The step you add to the designer to implement loop logic in a workflow. You'll define the loop parameters after adding it to the designer.

Adding the loop operator to the designer.


Specifies how to execute the items in the loop.


In or range


These are the steps you will execute for each iteration in the loop. When using an In loop, this might be a step to enrich the findings, send a message to an administrator with certain information, etc. When using a Range loop, this might be a Wait operator telling the system to pause while waiting for information or a certain outcome.


Sometimes, you only want to execute the previously discussed action if a certain condition applies. For example, when looping over an array of email addresses, you only want to execute the action if the email address belongs to a user in the VIP group.

Creating a condition for the If operator

Collect loop results

Torq has a dedicated step that collects the results of a loop's iterations. The step includes two parameters.

  • RESULT: The result of a single loop iteration. The result can be a string, number, JSON object, or array representing an individual entry of a collection of results.
  • FLATTEN_RESULTS: When true and RESULT is an array, merge that array into the array of collected loop values. Otherwise, it will add the RESULT as a single item in the collected loop results.


Terminate a loop

The Break operator is used within the loop to terminate the loop based on any specific condition before the loop comes to its predefined end. This means that while looping over an array or looping n times if a certain condition is met, terminate the loop.

Loop iteration limits

Loops can run up to 50,000 (fifty thousand) iterations before reaching the iteration limit. Once the iteration limit is reached, the Loop will fail and an appropriate error message will be displayed. 

How to use a loop

In many situations, you will use Torq to ingest data from a 3rd party vendor to perform further action. For example, you are ingesting data from your IAM vendor and using some of that data in your device-management vendor. Or, you are ingesting IP addresses from your threat intel system and enriching those addresses with additional information.

Additionally, you can loop over a data set and include a condition in the loop. If the condition is met, create a list of all of the objects for which the condition is true, and create a different or no list for the objects for which the condition is false.

In all of these examples, you will loop over the data set, and you will perform the same action for each item in that data set.

Loop on a dataset (Basic)

In this example, the workflow uses the In parameter to repeat an action on each member of an array. Follow the video tutorial or the step-by-step tutorial below.

This workflow is triggered by a scheduled task that pulls threat intel data from Recorded Future daily. You can substitute Recorded Future with your threat intel service.

Image of the entire workflow for the tutorial.

Pull IP addresses from threat intel vendor

When triggered, the workflow pulls IP addresses from the threat intel vendor, Recorded Future. Below is the output of the Search IP Addresses step.

Output of the threat intel results that shows several engines' results.

Extract IP addresses

Since the result of the Search IP Addresses step contains a bunch of additional information, you should use an extraction utility to extract all IPv4 addresses from the result. To extract the IP addresses, input the JSON path that points to $

Configuring the extract all IPv4 addresses step with output from the search IP addresses step.

The output of the extraction step provides you with a clean list of all IPv4 addresses from the event.

Showing the output (a list of IP addresses) of the extract IPv4 addresses step.

De-duplicate IP addresses

However, there are multiple entries for the same IP address. Passing the results of the extraction step, {{ $.extract_all_ipv4_addresses.results }} to the Unique IPs step de-duplicates the IP addresses.

Configuring the unique IPs step with output from the extract IPv4 addresses step.

The output provides you with a list of unique IP addresses from the event, meaning each IP address appears in the list only once.

Showing output (list of unique IP addresses) of the unique IP addresses step.

Loop over unique IP addresses

The list of unique IP addresses contains 10 items. To loop (perform a series of actions) on each of those IP addresses, the input for the Loop operator is the result of the Unique IPs step {{ $.unique_ips.result }}.

Configure parameters for the loop operator

Enrich each IP address with VirusTotal

The action performed on each IP address is IP enrichment using VirusTotal. You can substitute VirusTotal with your IP enrichment service.

Each IP address in the list is sent to VirusTotal. Every time an IP address is enriched is called an iteration. Each iteration results in a separate output entry in the execution log. Therefore, there are 10 entries in the execution log. This is easy to spot because they'll generally have the same or very close timestamps.

Showing results of a loop iteration with VirusTotal enrichment.

Use loop results (Intermediate)

In the previous example, a basic loop was established to iterate over an array of unique IP addresses and enrich each IP address with information from VirusTotal.

This example will show you how to use the results of the loop step in subsequent parts of the workflow.

Image of the entire workflow for the intermediate tutorial workflow.

Check if the IP is malicious

After enriching the IP addresses, you can check if the verdict of each IP address came back as malicious or benign. The following If step checks what the VirusTotal analysis showed.

Configuring the if operator where true condition is for malicious IPs.

Add malicious IPs to an array

 No action is required if all of the engines say that the IP address is clean. However, if at least one of the engines checked comes back with a malicious verdict, a Collect Loop Results utility step (name changed to Malicious IPs) can be used to add that IP address to an array. The array gets the name of the Collect Loop Results step that creates it. In this example, Malicious IPs.


Any IP addresses whose VirusTotal malicious score was at least 1 can be added to the Malicious IPs array by using $.value as the input for the Malicious IPs step (originally Collect Loop Results step).

Send malicious IPs to the admin

When the loop finishes and all malicious IP addresses were added to the Malicious IPs array, you can send a message with the findings to the admin whose email address is defined in the Set Admin Email Address step (originally Set Variable step).

Sending a Slack message to the admin with the list of malicious IP addresses.

Loop on a dataset (Advanced)

This final example will show how you can create a loop within a loop to further develop your flow. This will require you to manipulate the execution options of the Loop operator so you can access your data and act on it. In addition, this example demonstrates the usage of the Break operator.

The intermediate example showed that you can take the data on which you looped and further act on it, for example, adding the IP addresses to the malicious IP array.

This example will show how you can further examine the IP information and ‘double-check’ your findings to make sure the information you have is correct. You can cross-check the IP addresses that came back benign with another platform, and check if the IP address is associated with any problematic domains.

Image of the advanced workflow for the loop tutorial.

Get information on the domain associated with the benign IP

The IP address that came back as not malicious can be run through an additional engine, in this case, AbuseIPDB. In the IP field, you can use $.value as that contains the IP address from the initial loop, which came back as not malicious at a first glance.

Configuring the Abuse IPDB step with outputs from the loop operator.

AbuseIPDB provides the domain information for the IP address, which can be passed on to VirusTotal for a domain lookup.

Configuring the VirusTotal step that step that will get domain information for the IPs returned by Abuse IPDB.

Loop over the DNS records of a domain

VirusTotal returns all of the information about the domain, including the DNS records for the domain. You can loop over those records to check for a Type A.

It’s important to note that you can change the loop name and Execution Options to help identify each loop and make it easy to use each loop's Key and Value in context.

  • Change the Key name to key_dns_record.
  • Change the Value name to value_dns_record.

Configuring the loop operator to loop over the DNS records returned from the VirusTotal step.

Check if the domain is associated with other IPs

To act on the object you looped over, reference $.value_dns_record in subsequent steps.

Configuring the If operator with a true path for when a DNS record is Type A.

You can now create a condition whereby if the type under $.value_dns_record is A, the IP address of that record is checked. If the IP address for the Type A record is the same as the initial IP address that was already checked by VirusTotal, do nothing, as there is no point in checking the same IP address twice.

However, if the IP address is different from the initial address, the IP address in the type A DNS record should be checked by VirusTotal.

if the IP address is different from the initial address, the IP address in the type A DNS record should be checked by VirusTotal.

After handling the type A DNS record, you can break the loop since for the purposes of this tutorial, the assumption is there is only one type A record.

Add malicious IPs to an array

If the IP address comes back malicious, add the address to an array created by an additional Collect Loop Results step (name changed to Malicious IPs from Reverse DNS lookup). The array gets the name of the step that creates it. In our example, Malicious IPs from Reverse DNS lookup.

You need to add the original IP address that led us to the domain investigation to the Malicious IPs from Reverse DNS lookup array as well, as that IP address is linked to a compromised domain.


When you're done checking all IP addresses and domains you can add the Concat Arrays step (name changed to Malicious IPs and New Findings) to combine the initial malicious IPs with the most recent findings for a complete list of malicious IPs.

Configure the concat arrays step to combine the initial malicious IPs and recent findings.

Finally, send an updated list of all malicious IP addresses to the admin for further attention.

Loop for pagination

The following example demonstrates how to use Torq to retrieve multiple pages of results. It is based on retrieving alerts for a defined period of time and returning a set number of results in each iteration.

Since there are more results than the amount returned per iteration, we need to loop through the different pages until all of the results are retrieved.

Image of the workflow that implements pagination (retrieving multiple pages of results).

Query an EDR for a list of threats

The first thing you will do is set a time period 6 months ago. This is the earliest date from which you will pull alerts. Use the Get Date step (name changed to Get Date 6 months ago) with the optional parameter TIME_AGO to get the date 6 months prior.

Configuring the Get Date step to get dates six months prior.

Next, retrieve the threats from an EDR, in this case, SentinelOne.

Configuring the Sentinel One step to get a list of threats.

Query SentinelOne for all threats from the date you previously calculated, meaning 6 months ago. Set the LIMIT parameter of the SentinelOne Get Threats step to 7 to retrieve 7 threats each time the step is run (7 is only for purposes of this example).

After retrieving the threats, you can add the results to an array using the Collect Loop Results step (name changed to Threats). Since we are expecting additional pages of results, this step is useful for storing current, and future results in the same array.

Configuring the collect results step to collect results from the SentinelOne results.

Loop for pagination

Next, you can create a condition that checks within the results of the Get Threats step, if a value exists for the nextCursor parameter. If this parameter has a value, that indicates that there are additional results. If this parameter is empty or null, you know that all results have been retrieved.


If you look back at the Execution Log of the Get Threats step, you can see that the current results show a value for the nextCursor parameter.

Showing the next cursor key and value that indicates there are additional pages or results.

Since the condition evaluates as true, you can create a loop that runs either n times (in this example 500), or until the condition evaluates as false.

Using a Loop operator to check for additional pages of results.

The number 500 is, on the one hand, arbitrary - it could be 100, it could be 1000. However, since you may not know in advance how many results there are, you would rather have the loop set to a higher number since it will break on its own once all of the results have been retrieved.

Now run the Get Threats step again within the loop to retrieve the next batch of results. You do this by passing the nextCursor parameter in the step.

Adding the Get Threats step within the loop for additional pages to check for additional pages.

The results are appended to the array you created earlier with the Collect Loop Results step (name changed to Threats) as long as both steps have the same name.

The results are appended to the array you created earlier with the Collect Loop Results step.

Lastly, you will add a condition to again check if there is a value in the nextCursor parameter. If there is, you will continue to loop to retrieve the next batch of results. Once the nextCursor comes back with a null value, the loop breaks before completing the 500 iterations as there are no more results to retrieve.

Loop for polling

The following example shows how to use Torq to perform polling, i.e. waiting for a process to complete before moving on with your workflow. It is based on extracting and analyzing URLs from a suspected phishing email.

Since the analysis, and the respective report, can take some time to complete, you will need to implement a loop waiting for the results.

Image of a workflow for implementing polling.

Extract URLs from a suspicious email

In this example workflow, the trigger is an email that comes from a user asking for the email to be analyzed. This occurs when a user suspects the email might be part of a phishing scam.

Configuring an IMAP integration to ingest suspicious emails.

As with previous workflows, you can set a variable containing the contact information of an admin so they can be contacted within the process.

Configuring the admin email address to use for sending Slack messages later.

Use one of the extraction utilities to extract all of the URLs from the suspected email. To do so, you must set the input as the email body.

Configure the extract all URLs step to extract from the ingested emails.

Submit extracted URLs for analysis

When you have all of the URLs, you can start to loop over each one, using a loop over a dataset (more information on those is available in earlier tutorials). In the input for the loop, you should enter the results of the extraction utility step.

Configuring a loop operators to loop over URLs extracted from an email.

The action you want to take on each of these URLs is scanning with VirusTotal. Enter the value from the key:value pair of the loop step as the URL to scan. In this example, that is $.loop_2_value.

Configure the scan URL VirusTotal step that will scan the URLs as part of the loop.

Wait for analysis results retrieval to complete

Create another loop within this loop using the Range parameter. In this example, the loop runs a maximum of 20 times (or less if the results are retrieved sooner).

Configuring a loop operator with the range parameter to run 20 times or until the results are retrieved.

You will then pass the ID of the URL scan to a Get Analysis Results step. This attempts to retrieve the report for the URL that was submitted earlier.

If the analysis has completed, you can check the results to determine if the URL is malicious. Otherwise, configure the workflow to wait a few seconds (in this case, 10) before checking again. Once that is completed, you can move back to the outer loop and check the next URL.

Notify the admin of investigation results

After all of the URLs are checked, you can create a condition determining which message to send to the admin you defined earlier.

If the Collect Malicious URLs array has any values in it, that means the email was problematic and requires further investigation. However, if all of the URLs were ok, you can send a message that everything is ok.

Sending a Slack message to the admin with the malicious results.

Was this article helpful?

What's Next
Changing your password will log you out immediately. Use the new password to log back in.
First name must have atleast 2 characters. Numbers and special characters are not allowed.
Last name must have atleast 1 characters. Numbers and special characters are not allowed.
Enter a valid email
Enter a valid password
Your profile has been successfully updated.