Skip to content

FOREACH CI

Walks a list of resource identifiers held in the stash, loads each one as a live resource, and runs the ops nested underneath it once per entry with that resource in a loop variable.

The loading is the point. A list of identifiers on its own is a list of strings; after this op each entry is the resource itself, so nested ops can read its fields with ${myitem.name} and ops that expect a resource can take it directly. If all you need is to walk a list of plain strings, use FOR eval or one of the file loops instead.

the source variable foo_mid bar_mid name:baz all loaded up front the loop variable holds one resource run the nested ops next entry one identifier that does not resolve stops the rule before the first pass runs

Settings

This op has no configuration form. Its Config tab holds a YAML editor carrying the op's two settings, which you edit as text and save with the rest of the op's properties. A freshly dropped op opens on this:

local_var: value
variable: stash_var

Both values are placeholders and neither is likely to match anything in your rule, so an op you drop and never open does nothing at all. The two keys come back sorted alphabetically each time you open the tab, whatever order you typed them in, and YAML comments are dropped when you save. Invalid YAML raises a dialog and blocks the save.

variable

The name of the stash variable holding the identifiers. A bare name, not ${stash_var}. Dots walk into nested data, so job.servers works.

The value it points at can be a list, a single identifier on its own, or a comma-separated string. Commas are split for you and spaces around them are dropped, so foo_mid, bar_mid and a two-entry list behave identically.

Each entry may be a resource identifier, a resource that is already loaded, or a field:value lookup, for example name:bar_server or moniker:baz. An entry that is already a resource is reloaded from storage, so it reflects what is stored now rather than what was read earlier in the rule. A field:value lookup searches every resource in the system rather than one class, so a name that two resources share stops the rule with a message listing both identifiers.

Leave it blank, or point it at a variable that does not exist, and the loop runs zero times. Nothing is logged and nothing fails. That silence is the first thing to check when a FOREACH CI block appears to be ignored.

local_var

The name of the loop variable the current resource is placed in. Delete the line and it falls back to value, which collides with almost everything, so give it a name of its own.

A variable of that name that already existed is shadowed for the duration of the loop and put back when the last pass finishes. Anything you want to survive the loop has to be collected into a different variable, with PUSH VAR or SET VAR.

Inside the body, read the resource's fields through it: ${myserver.name}, ${myserver.hostname}. Static Analysis knows this variable is defined inside the block, so uses of it in the nested ops are not reported as an unknown variable.

Loading and failure

Every entry is turned into a resource before the first pass begins, not lazily as the loop reaches it. One identifier that no longer exists stops the rule immediately, naming the identifier it could not find, and none of the valid entries ahead of it get processed. Filter the list before the loop when it may contain stale identifiers.

A failure inside the body ends the loop as well as the job. The remaining entries are never visited. Put TRY statement and CATCH statement inside the loop when one bad entry should not stop the others.

The loop writes nothing back. It hands the body a resource loaded from storage, and the body has to save any change of its own; use Invoke Resource methods for that. Return Key on the Options tab has no effect on this op, because the loop has no result to hand back.

The job log gets one debug line for the op as a whole and nothing per pass. Add LOG Message in the body when you want to see which entry is being handled.

Combining with other ops

Build the list first. SET VAR to CI puts a resource into a variable, PUSH VAR grows a list one entry at a time, and services that select resources leave their results in the stash for this op to walk.

Feed the loop variable straight into an op that takes a server or another resource, such as Run a Remote Script or Ship File Remotely.

Skip entries with IF var condition THEN inside the body, testing a field of the loop variable.

For the identifiers themselves, see MID.

Examples

Restart a service on every server a previous op collected.

local_var:  myserver
variable:   bar_servers
  -> LOG Message            restarting on ${myserver.name}
  -> Run a Remote Script    server ${myserver}

Walk the projects a rule gathered and act on each one.

local_var:  myproject
variable:   foo_projects
  -> IF var condition THEN
       When: All
         myproject.name   NOT LIKE   ^sandbox
       -> Run a Remote Script