Skip to main content

Using .NET Variables to Partially Automate BimlScript

Variables are the first step toward automation. Instead of copying and editing Biml code for each table, you define the varying parts as variables and change only those values. This simple technique dramatically reduces the time to create repetitive packages.

What You'll Learn

  • Declare C# variables in control nuggets
  • Insert variable values using text nuggets
  • Use multiple variables for schema, table, and connection names
  • Work with different variable types (strings, integers, booleans)
  • Apply variable naming best practices

Prerequisites: Introduction to BimlScript (understand code nugget types)


Why Use Variables?

Before exploring the technique, consider the time savings:

ApproachTime per Package30 Packages Total
Manual SSDT15 minutes7.5 hours
Plain Biml (copy/paste)5 minutes2.5 hours
BimlScript variables15 seconds7.5 minutes

Variables don't eliminate repetition entirely—you still change the variable value and rebuild for each package. But that 15-second change-and-build cycle is dramatically faster than editing package properties manually.

The next lesson on control structures removes even this manual step, generating all packages in one build.


The Scenario

You need to copy data from 30 tables on a source server to matching tables on a target server. The servers aren't linked, so SSIS is a natural choice.

The challenge: Creating 30 nearly identical packages manually is tedious and error-prone. Each package follows the same pattern—only the table name changes.

The solution: Create a template with variables for the changing parts. Change the variable, rebuild, and get a customized package in seconds.


Step 1: Create a Working Template

Start with actual working Biml code. Don't add automation until you have a single package that builds and runs correctly.

<Biml xmlns="http://schemas.varigence.com/biml.xsd">
<Connections>
<OleDbConnection Name="Source"
ConnectionString="Provider=SQLNCLI11;Server=SOURCE_SERVER;Initial Catalog=AdventureWorksDW2012;Integrated Security=SSPI;" />
<OleDbConnection Name="Target"
ConnectionString="Provider=SQLNCLI11;Server=localhost;Initial Catalog=AdventureWorksDW2012;Integrated Security=SSPI;" />
</Connections>
<Packages>
<Package Name="Copy DimAccount" ConstraintMode="Parallel">
<Tasks>
<Dataflow Name="DFT_CopyData">
<Transformations>
<OleDbSource Name="Retrieve Rows" ConnectionName="Source">
<DirectInput>SELECT * FROM dbo.DimAccount</DirectInput>
</OleDbSource>
<OleDbDestination Name="Load Rows" ConnectionName="Target">
<ExternalTableOutput Table="dbo.DimAccount" />
</OleDbDestination>
</Transformations>
</Dataflow>
</Tasks>
</Package>
</Packages>
</Biml>

Build this and verify you get a valid SSIS package. Once it works, you're ready to add variables.


Step 2: Identify Values to Replace

Look at your template and find the values that change for each table:

LocationCurrent ValueWhat Changes
Package Name attribute"Copy DimAccount"Table name
SELECT statement"dbo.DimAccount"Schema and table
ExternalTableOutput Table"dbo.DimAccount"Schema and table

In this scenario, three places reference the table name. A variable lets you change all three by editing one line.


Step 3: Declare Variables

Add a control nugget at the top of your file to declare variables. Control nuggets use <# ... #> syntax:

Single Variable

<# var tableName = "DimAccount"; #>

This declares a C# variable named tableName with the value "DimAccount". The var keyword lets C# infer the type (string in this case).

Multiple Variables

For more flexibility, use multiple variables:

<#
var schemaName = "dbo";
var tableName = "DimAccount";
var sourceConn = "Source";
var targetConn = "Target";
#>

Why separate schema and table? Source and target schemas often differ. Your source might use Sales.Customer while your staging area uses stg.Customer.

Variable Naming Best Practices

PracticeGoodAvoid
Use camelCasetableNameTableName, table_name
Be descriptivesourceSchemaNames, schema1
Avoid reserved wordstableNametable, string
Group related variablesKeep togetherScatter throughout file

Step 4: Use Text Nuggets to Insert Values

Text nuggets use <#= ... #> syntax. The equals sign is critical—it tells BimlScript to output the expression's value:

<Package Name="Copy <#=tableName#>">

When BimlScript processes this, it replaces <#=tableName#> with DimAccount, producing:

<Package Name="Copy DimAccount">

Complete Example

Here's the full template with variables:

<#
// Configuration variables - change these values to generate different packages
var schemaName = "dbo";
var tableName = "DimAccount";
#>
<Biml xmlns="http://schemas.varigence.com/biml.xsd">
<Connections>
<OleDbConnection Name="Source"
ConnectionString="Provider=SQLNCLI11;Server=SOURCE_SERVER;Initial Catalog=AdventureWorksDW2012;Integrated Security=SSPI;" />
<OleDbConnection Name="Target"
ConnectionString="Provider=SQLNCLI11;Server=localhost;Initial Catalog=AdventureWorksDW2012;Integrated Security=SSPI;" />
</Connections>
<Packages>
<Package Name="Copy_<#=schemaName#>_<#=tableName#>" ConstraintMode="Parallel">
<Tasks>
<Dataflow Name="DFT_CopyData">
<Transformations>
<OleDbSource Name="Retrieve Rows" ConnectionName="Source">
<DirectInput>SELECT * FROM [<#=schemaName#>].[<#=tableName#>]</DirectInput>
</OleDbSource>
<OleDbDestination Name="Load Rows" ConnectionName="Target">
<ExternalTableOutput Table="[<#=schemaName#>].[<#=tableName#>]" />
</OleDbDestination>
</Transformations>
</Dataflow>
</Tasks>
</Package>
</Packages>
</Biml>

Workflow:

  1. Set tableName = "DimAccount", build → Copy_dbo_DimAccount.dtsx
  2. Set tableName = "DimCurrency", build → Copy_dbo_DimCurrency.dtsx
  3. Repeat for remaining tables

Each iteration takes about 15 seconds instead of 15 minutes.


Beyond Simple Strings

Variables aren't limited to strings. Here are other useful types:

Integer Variables

Control batch sizes, row counts, or numeric configurations:

<#
var batchSize = 10000;
var maxErrors = 100;
#>
<OleDbDestination Name="Load Rows" ConnectionName="Target" MaximumInsertCommitSize="<#=batchSize#>">

Boolean Variables for Conditionals

Use booleans to enable or disable features:

<#
var truncateFirst = true;
var tableName = "DimAccount";
#>
<Tasks>
<# if (truncateFirst) { #>
<ExecuteSQL Name="Truncate Target" ConnectionName="Target">
<DirectInput>TRUNCATE TABLE [stg].[<#=tableName#>]</DirectInput>
</ExecuteSQL>
<# } #>
<Dataflow Name="DFT_CopyData">
<!-- Data flow content -->
</Dataflow>
</Tasks>

When truncateFirst is true, the package includes a truncate step. When false, it skips directly to the data flow.

DateTime Variables for Dynamic Naming

Add timestamps to package names or log entries:

<#
var buildDate = DateTime.Now;
var tableName = "DimAccount";
#>
<Package Name="Copy_<#=tableName#>_<#=buildDate.ToString("yyyyMMdd")#>">

This produces names like Copy_DimAccount_20240115.dtsx.

String Methods

Apply transformations to variable values:

<#
var tableName = "DimAccount";
#>
<!-- Uppercase: <#=tableName.ToUpper()#> produces "DIMACCOUNT" -->
<!-- Lowercase: <#=tableName.ToLower()#> produces "dimaccount" -->
<!-- Substring: <#=tableName.Substring(3)#> produces "Account" -->

Platform Considerations

While this guide focuses on SSIS package generation, the variable replacement technique applies to all Biml-supported platforms:

PlatformVariable Usage
SSISPackage names, connection strings, table references
ADFPipeline names, dataset references, linked service names
DatabricksNotebook parameters, table paths, schema names
SnowflakeStage names, warehouse references, schema paths
Microsoft FabricLakehouse paths, notebook parameters

BimlFlex extends these patterns with a metadata-driven UI, eliminating manual variable changes entirely. See BimlFlex Documentation for enterprise-scale automation.


Common Mistakes and Fixes

1. Missing Equals Sign in Text Nugget

Wrong:

<Package Name="Copy <#tableName#>">

Right:

<Package Name="Copy <#=tableName#>">

Without the =, BimlScript treats it as a control nugget (code to execute) rather than a text nugget (value to output). The package name becomes literally "Copy " with nothing after it.

2. Variable Used Before Declaration

Wrong:

<Package Name="Copy <#=tableName#>">
<# var tableName = "DimAccount"; #>

Right:

<# var tableName = "DimAccount"; #>
<Package Name="Copy <#=tableName#>">

Variables must be declared before use. BimlScript processes top-to-bottom.

3. Case Sensitivity

Wrong:

<# var TableName = "DimAccount"; #>
<Package Name="Copy <#=tablename#>">

Right:

<# var tableName = "DimAccount"; #>
<Package Name="Copy <#=tableName#>">

C# is case-sensitive. TableName and tablename are different variables.

4. Missing Quotes Around String Values

Wrong:

<# var tableName = DimAccount; #>

Right:

<# var tableName = "DimAccount"; #>

String literals require double quotes in C#.

5. Forgetting to Rebuild

After changing variable values, you must rebuild to generate updated packages. The Biml file is a template—changes don't apply until you run the build process.


Quick Reference

Variable Declaration Syntax

SyntaxDescriptionExample
<# var x = "value"; #>Declare string variable<# var tableName = "Customer"; #>
<# var x = 100; #>Declare integer variable<# var batchSize = 10000; #>
<# var x = true; #>Declare boolean variable<# var truncateFirst = true; #>

Value Insertion Syntax

SyntaxDescriptionExample
<#=variableName#>Insert variable value<#=tableName#>
<#=var.Method()#>Call method on variable<#=tableName.ToUpper()#>
<#=expression#>Insert expression result<#=batchSize * 2#>

Common String Methods

MethodInputOutput
.ToUpper()"Customer""CUSTOMER"
.ToLower()"Customer""customer"
.Length"Customer"8
.Replace("a","b")"Data""Dbtb"
.Substring(0,3)"Customer""Cus"

Next Steps