Tutto ciò che in un playbook FortiSOAR è dinamico passa da Jinja2: i campi degli step, le condizioni, i messaggi, perfino picklist e checkbox. Ovunque compaia l’icona Dynamic Values puoi sostituire il valore fisso con un’espressione. Capire come il motore costruisce il contesto su cui quelle espressioni girano è ciò che separa un playbook che funziona da uno che fallisce con un errore incomprensibile.

Due sintassi, non una

Jinja ha due delimitatori e confonderli costa il punto intero. Le doppie graffe valutano un’espressione e restituiscono un valore; la coppia graffa più percentuale introduce un’istruzione di controllo — for, if, elif, else — che da sola non restituisce niente.

{{ vars.users.Alvian }}
{% if vars.severity == "Critical" %}...{% endif %}
{% for item in vars.teamName %}...{% endfor %}

Un if che non prevede il ramo else fa fallire il playbook quando la condizione è falsa: restituisce stringa vuota, e se il campo di destinazione non accetta il vuoto lo step va in errore. Prevedi sempre l’else, anche solo per scrivere None.

I filtri sono case-sensitive. Dalla 7.4.0 il motore gira in sandbox e blocca l’accesso ai membri privati: un’espressione che usa i doppi underscore fallisce con l’errore CS-WF-7 sul template Jinja non valido, e la strada giusta è riscriverla — per esempio usando update al posto della scrittura diretta di una chiave.

Attenzione infine alla conversione automatica dei tipi: il motore deduce il tipo dal valore. Un join su una lista di interi restituisce ancora una lista; se ti serve una stringa devi racchiudere l’espressione fra virgolette.

{{vars.data | join(',')}}
"{{vars.data | join(',')}}"

Il JSON dell’ambiente: chi popola cosa

Le variabili ordinarie vivono nel namespace vars, e il motore ne popola alcune da solo. In vars.input.records trovi i record che hanno fatto scattare il playbook, per i trigger di database e per il Manual. Nei parametri di input, alla chiave api_body, trovi il corpo della richiesta di un Custom API Endpoint. In vars.request.headers ci sono gli header dell’ambiente, fra cui X-RUNBYUSER, che restituisce chi ha lanciato il playbook. E sotto vars.steps, con il nome dello step, c’è l’output degli step già eseguiti.

L’accesso è quello di un dizionario Python: notazione con il punto e notazione con le parentesi quadre sono equivalenti, ma se una chiave contiene caratteri speciali le parentesi sono l’unica strada.

{{ vars.users.Kreb.pi }}
{{ vars.users['Kreb']['pi'] }}

Dettaglio che in esame vale l’intero punto: una chiave inesistente non restituisce stringa vuota, restituisce un errore del tipo “no such element”. Da qui l’abitudine di verificare prima di leggere, con is defined oppure confrontando con None.

Riferirsi al risultato di uno step precedente

La forma è sempre la stessa: vars.steps, il nome dello step con gli spazi sostituiti da underscore, poi la chiave del risultato.

{{vars.steps.Manual_Input_Test.record}}
{{vars.steps.Find_all_Open_Alerts[0].name}}

Il punto in cui si sbaglia più spesso è l’indice. Se l’output dello step è un array devi indicare la posizione, e l’indice parte da zero: il quarto elemento è indice 3. La finestra Dynamic Values genera l’espressione con indice 0, quindi se ti serve un altro elemento la correggi a mano prima di eseguire.

La scheda Step Results mostra lo schema di output degli step già collegati. L’opzione Show Last Run Result if Available usa i dati dell’ultima esecuzione reale invece dello schema predefinito, ma quei dati esistono solo se il playbook è girato in modalità DEBUG. Stessa condizione per il Jinja Editor, sotto Tools nel designer: l’elenco delle ultime esecuzioni, al massimo 30, e il pulsante Load Env JSON compaiono solo per playbook già eseguiti in DEBUG. Dalla 7.6.0 l’ENV caricato include anche vars.steps, e nell’editor di codice degli step è arrivato l’IntelliSense.

Il Jinja Editor ha tre aree — Template, JSON e Output — e serve esattamente a questo: provare un’espressione su un JSON di esempio e vedere il risultato prima di metterla nel playbook.

Custom, global e la vista semplificata

Le variabili custom nascono con lo step Set Variable e hanno scope locale: valgono nei restanti step del playbook e nei figli, a qualunque profondità. Le global variable si creano solo dal designer, con Tools e poi Global Variable, e si usano ovunque; il nome deve iniziare con una lettera e contenere solo lettere e numeri, senza spazi né caratteri speciali. Le custom si leggono sotto vars, le global sotto globalVars.

{{vars.SPF_Record}}
{{globalVars.CICD_Repositories}}

Esiste poi una lista di parole riservate che non puoi usare come nome di variabile: fra le altre input, request, result, items, values, keys, message, env, when, for_each, do_until e ignore_errors. Sceglierne una non dà un errore in fase di salvataggio, dà un comportamento sbagliato a runtime.

Un’ultima cosa che disorienta chi arriva da versioni precedenti: la Simplified Expression View, attiva per impostazione predefinita, mostra le espressioni come tag colorati anziché come Jinja completo, con colori diversi per variabili custom, global, risultati di step e input. Passando il mouse sul tag vedi l’espressione vera. I frammenti di codice, cioè le istruzioni con graffa e percentuale, non vengono convertiti in tag e restano visibili per intero: se in un campo vedi Jinja lunga mentre altrove vedi tag, non è un bug.