Skip to main content

Using Biml Annotations

Why Annotations Matter

A Biml annotation is a tag that attaches arbitrary text to a generated object. The text alone is not the interesting part. The value comes from reading that text in a later script to drive decisions, such as filtering packages by source system or grouping them into a master package.

This walkthrough shows the two halves of that workflow: tagging packages on creation, then reading the tags from another script.

Tagging Packages on Creation

The first script generates four placeholder packages. Each package carries an annotation with the tag 'ENV' and the value 'Test'. The script declares 'tier="1"' so it runs first.

<#@ template language="C#" tier="1" #>
<# string envLabel = "Test"; #>
<Biml xmlns="http://schemas.varigence.com/biml.xsd">
<Packages>
<# for (int i = 1; i < 5; i++) { #>
<Package Name="LoadFeed<#= i.ToString() #>" ConstraintMode="Linear">
<Annotations>
<Annotation Tag="ENV"><#= envLabel #></Annotation>
</Annotations>
</Package>
<# } #>
</Packages>
</Biml>

When the script compiles, every package in the expanded Biml carries the 'ENV' tag with the value 'Test'.

Reading Annotations in a Later Script

A second script runs at 'tier="2"' so it sees every package the first script produced. It iterates through 'RootNode.Packages', calls 'GetTag' to read the 'ENV' value, and adds an Execute Package Task for every package whose tag matches 'Test'.

<#@ template language="C#" tier="2" #>
<Biml xmlns="http://schemas.varigence.com/biml.xsd">
<Packages>
<Package Name="MASTER_TestEnv" ConstraintMode="Linear">
<Tasks>
<# foreach (var childPackage in RootNode.Packages) {
if (childPackage.GetTag("ENV") == "Test") { #>
<ExecutePackage Name="Run <#= childPackage.Name #>">
<Package PackageName="<#= childPackage.Name #>" />
</ExecutePackage>
<# } } #>
</Tasks>
</Package>
</Packages>
</Biml>

The compiled output is a master package that calls every child package whose 'ENV' annotation is 'Test'.

Why Not Just Encode the Value in the Package Name?

Iterating over 'RootNode.Packages' and matching on 'childPackage.Name' produces the same result for a single attribute. The naming approach falls apart as soon as additional metadata enters the picture, such as creator, ETL stage, or version, because the package name has to encode every dimension that any downstream script might care about. Annotations let each piece of metadata live in its own tag and be queried independently.

Where to Use This

The same pattern fits any scenario that needs to filter or group generated objects: building per source orchestrators, emitting documentation grouped by ETL stage, or tagging packages with deployment targets so a downstream script can build environment specific deployment scripts. The tag itself is just a string. The pattern is what gives it value.