Output Engine

Using Tera Templates

Generate structured output resources from graph data using Tera's text templating, conditional logic, and relationship traversal.

Using Tera Templates

Tera is the primary templating engine for [[output]] rules. It is ideal for generating JSON output where the structure is straightforward and the data comes directly from the origin_resource and its related graph nodes.

For the complete Tera syntax reference and rescile-specific filters, see Tera Function & Filter Reference and Templating & Data.


Worked Example: Service Summary

This example generates a new service_summary resource for every application that is on the edge network. Each summary is made available as a downloadable JSON file.

data/output/service_summary.toml

origin_resource = "application"

# Define some static data available to the template
output_metadata = { version = "1.1", author = "security-team" }

[[output]]
resource_type = "service_summary"
name = "summary-for-{{ origin_resource.name }}"
filename = "summary-{{ origin_resource.name }}.json"
mimetype = "application/json"
match_on = [
  { property = "network_zone", value = "edge" }
]
# The template uses Tera to generate a JSON object, including conditional logic.
# All variables from the context (`origin_resource`, `output_metadata`) are available.
template = '''
{
  "output_author": "{{ output_metadata.author }}",
  "application_name": "{{ origin_resource.name }}",
  "owner": "{{ origin_resource.has_responsibility[0].node.name }}",
  "hosting_details": {
    "platform": "{{ origin_resource.runs_on[0].node.name }}",
    "criticality": "{% if origin_resource.environment == 'prod' %}"high"{% else %}"low"{% endif %}"
  }
}
'''
  • Graph Impact:
    1. The importer finds all application resources that match the match_on criteria (e.g., app-1 and app-2).

    2. It creates a single new resource of type service_summary. This resource’s primary key (name) is also set to service_summary.

    3. For each matching application (app-1, app-2): a. It renders the name template to get the primary key for a new resource (e.g., summary-for-app-1). b. It renders the template, which produces a JSON object string. c. It parses this JSON string. d. It creates a new resource of type service_summary with the generated name. The keys and values from the parsed JSON become the properties of this new resource.

    4. For app-1, a new service_summary resource is created with properties like:

      {
        "name": "summary-for-app-1",
        "filename": "summary-app-1.json",
        "mimetype": "application/json",
        "output_author": "security-team",
        "application_name": "app-1",
        "owner": "team-alpha",
        "hosting_details": { ... }
      }
      
    5. A similar resource is created for app-2.

    6. Finally, a DESCRIBED_BY relationship is created from each matching application (app-1, app-2) to its corresponding new service_summary resource.


Traversing Relationships in Templates

The origin_resource variable exposes related nodes through dot notation. Multi-valued relations are arrays; access a specific item with [0] or iterate with {% for ... %}.

origin_resource = "application"

[[output]]
resource_type = "dependency_report"
name = "deps-{{ origin_resource.name }}"
filename = "deps-{{ origin_resource.name }}.json"
mimetype = "application/json"
template = '''
{
  "application": "{{ origin_resource.name }}",
  "primary_database": "{{ origin_resource.database[0].name | default(value='none') }}",
  "all_databases": {{ origin_resource.database | map(attribute="name") | json_encode | safe }},
  "hosting_platform": "{{ origin_resource.cloud_provider[0].name
                           | default(value=origin_resource.onprem_datacenter[0].name
                           | default(value='unknown')) }}"
}
'''

Tip: Use | default(value="fallback") to avoid rendering errors when a related node does not exist for a particular origin_resource.


Conditional Logic and Loops

Tera’s {% if %} and {% for %} blocks work identically inside template values:

template = '''
{
  "name": "{{ origin_resource.name }}",
  "tier": "{% if origin_resource.environment == 'prod' %}gold{% else %}silver{% endif %}",
  "tags": [
    {% for tag in origin_resource.tags %}
    "{{ tag }}"{% if not loop.last %},{% endif %}
    {% endfor %}
  ]
}
'''

Using Header Variables

Top-level TOML keys defined in the file header are available as template variables alongside origin_resource:

origin_resource = "application"

output_metadata = { version = "2.0", author = "platform-team" }

[[output]]
resource_type = "versioned_summary"
name = "vs-{{ origin_resource.name }}"
filename = "vs-{{ origin_resource.name }}.json"
mimetype = "application/json"
template = '''
{
  "schema_version": "{{ output_metadata.version }}",
  "generated_by":   "{{ output_metadata.author }}",
  "application":    "{{ origin_resource.name }}"
}
'''

For loading data from external JSON files, see Templating & Data — External JSON Files.