Linking Resources
Using
[[link_resources]]: A “Pull” Operation
The [[link_resources]] directive is a powerful “pull” operation, analogous to a database JOIN or a lookup function. It enriches the origin_resource by finding a remote resource based on a join condition and pulling data from it.
Unlike [[copy_property]] (a “push” operation that requires an existing connection), [[link_resources]] actively searches for the remote resource. It is the primary tool for enriching a resource with data from a central “lookup” table (e.g., joining a server with a central location database) or for creating explicit relationships based on shared keys.
How [[link_resources]] Works
| Key | Mandatory | Description |
|---|---|---|
with |
Yes | The type of the “remote” resource to find and join with. |
join |
No | A table defining the join condition: { local = "prop_on_origin", remote = "prop_on_with" }. If property names are the same, a simple string can be used. |
copy_properties |
No | An array defining properties to copy from the remote with resource to the origin_resource. Supports simple strings, rename syntax ({ from = "...", as = "..." }), and transformation with a Tera template. |
create_relation |
No | A table { type = "RELATION_TYPE" } to create a new relationship from the origin_resource to the with resource. |
match_on |
No | An array of filter objects to apply to the origin_resource set. |
match_with |
No | An array of filter objects to apply to the with resource set. This is the counterpart to match_on. It supports advanced expression filters that can compare properties between the origin_resource and a candidate target_resource from the with set. |
[[link_resources]] Patterns
The directive supports distinct patterns depending on which keys are used.
1. Precise Property-Based Joins (join)
This is the most common pattern, used for linking resources based on a shared identifier (like a foreign key).
Goal: For each server, find its corresponding application by matching server.app_id to application.id, copy the application’s owner property, and create a RUNS relationship.
origin_resource = "server"
[[link_resources]]
with = "application"
join = { local = "app_id", remote = "id" }
copy_properties = [
{ from = "owner", as = "server_owner" },
{ from = "version", as = "major_version", template = "{{ value | split(pat='.') | first }}" }
]
create_relation = { type = "RUNS" }
- Graph Impact: A
serverwithapp_id: "app-123"will be joined with anapplicationthat hasid: "app-123". The server will gain aserver_ownerproperty and amajor_versionproperty (e.g.,"2"if the application’s version was"2.7.1"). A new(server) -[RUNS]-> (application)relationship will also be created.
2. Expression-Based Joins
For complex join conditions that go beyond simple key equality, you can use an expression in the match_with block. This provides access to both the origin_resource (the resource being processed in the main iteration) and a special target_resource variable.
The target_resource variable refers to a candidate resource from the with set that is currently being evaluated as a potential match. For each origin_resource, rescile iterates through all resources of the with type, evaluating the expression for each (origin_resource, target_resource) pair. The first pair that causes the expression to render "true" is considered a match.
Goal: For each vm_request (origin_resource), find a physical_host (with resource) that has sufficient available_ram. The target_resource variable refers to the physical_host being checked.
origin_resource = "vm_request"
[[link_resources]]
with = "physical_host"
match_with = [
{ expression = "{% if origin_resource.requested_ram <= target_resource.available_ram %}true{% endif %}" }
]
copy_properties = [ { from = "name", as = "assigned_host" } ]
create_relation = { type = "HOSTED_ON" }
- Graph Impact: For each
vm_request,rescilewill iterate through allphysical_hostresources. Theexpressioncomparesvm_request.requested_ramwithphysical_host.available_ram. The first host that satisfies the condition will be linked. This pattern is ideal for resource allocation or policy-based linking.
3. Filtered Set Linking (N:M Joins)
This pattern links groups of resources based on filters, not on shared property values. It creates a relationship from every resource in the filtered source set to every resource in the filtered destination set.
Goal: Link all production applications to the central production_gateway.
origin_resource = "application"
[[link_resources]]
match_on = [ { property = "environment", value = "production" } ]
with = "gateway"
match_with = [ { property = "name", value = "production_gateway" } ]
create_relation = { type = "ROUTES_THROUGH" }
- Graph Impact: Every
applicationresource withenvironment: "production"will get a newROUTES_THROUGHrelationship pointing to theproduction_gateway.
4. Unconditional “Singleton” Join
By omitting on, join, match_on, and match_with, you can link every origin_resource to a single, globally unique resource type.
Goal: Link every server resource to the single, central subscription resource.
origin_resource = "server"
[[link_resources]]
with = "subscription"
create_relation = { type = "PART_OF" }
- Graph Impact: Every
serverin the graph will now have aPART_OFrelationship pointing to thesubscriptionresource. This is a highly efficient way to establish a central point of reference.
Comparison of Linking Patterns
| Feature | on / join |
Expression-Based Join | Filtered Set Linking (match_on / match_with) |
Unconditional (“Singleton”) |
|---|---|---|---|---|
| Join Logic | Value Equality: local.prop == remote.prop |
Arbitrary Expression: f(origin, target) -> bool |
Set Intersection: Links filtered sets | Unconditional: Links all sources to remote(s) |
| Cardinality | Typically 1:1 or 1:N | Typically 1:1 or 1:N | Can be 1:1, 1:N, N:1, or N:M | N:1 (most common) |
| Primary Use Case | Foreign-key style relationships based on shared identifiers. | Resource allocation or complex policy-based joins. | Applying broad, policy-based connections between groups of resources. | Connecting all resources to a central, global entity (e.g., a subscription). |
[[copy_property]] vs. [[link_resources]]: Push vs. Pull
While both directives can add properties to a resource, they serve fundamentally different purposes based on their “push” vs. “pull” nature.
| Feature | [[copy_property]] |
[[link_resources]] (with copy_properties) |
|---|---|---|
| Operation | Push: Pushes data from the origin_resource to a connected node. |
Pull: The origin_resource pulls data from a remote node. |
| Requirement | An existing, direct connection must be present. | No existing connection required. It finds the remote node via a join condition. |
| Analogy | Property Assignment / Inheritance: connected_object.property = self.property |
Database JOIN / Lookup: self.property = lookup_object(condition).property |
| Use Case | Propagating context along an existing relationship graph (e.g., an application pushing its environment to its server). |
Enriching a resource with data from a central “lookup” source (e.g., a server looking up its location from a datacenter resource). |