Using Modules
Learn how to organize Carina infrastructure code into reusable modules with arguments, attributes, directory modules, and nested module loading.
Modules let you group related resources into reusable units. A module defines its inputs with arguments, its outputs with attributes, and can be loaded with use and called from any .crn file.
Creating a module
A module is a directory containing one or more .crn files. Every .crn file in the directory is merged together — no filename (including main.crn) is privileged. Single-file modules are not supported; always point use at a directory.
Create a file at modules/network/main.crn (the filename is up to you):
arguments {
cidr_block : String
subnet_cidr: String
az : String
}
let vpc = awscc.ec2.Vpc {
cidr_block = cidr_block
tags = {
Name = 'module-vpc'
}
}
let subnet = awscc.ec2.Subnet {
vpc_id = vpc.vpc_id
cidr_block = subnet_cidr
availability_zone = az
tags = {
Name = 'module-subnet'
}
}
attributes {
vpc_id : awscc.ec2.Vpc = vpc.vpc_id
subnet_id: awscc.ec2.Subnet = subnet.subnet_id
}
Arguments block
The arguments block declares the inputs the module accepts. Each parameter has a name and a type:
arguments {
cidr_block: String
env_name : String
}
Supported types include String, Bool, Int, Float, list(String), map(String), and resource type references like awscc.ec2.Vpc.
Attributes block
The attributes block declares the outputs the module exposes to callers:
attributes {
vpc_id : awscc.ec2.Vpc = vpc.vpc_id
subnet_id: awscc.ec2.Subnet = subnet.subnet_id
}
Each attribute has a name, an optional type annotation, and a value expression.
Loading and calling a module
From your root configuration, load the module with use and call it with arguments:
provider awscc {
region = awscc.Region.ap_northeast_1
}
let network = use { source = './modules/network' }
network {
cidr_block = '10.0.0.0/16'
subnet_cidr = '10.0.1.0/24'
az = 'ap-northeast-1a'
}
The use expression loads the module from its source directory and binds it to a name. You then call the module like a function, passing its arguments as a block.
Accessing module attributes
To use a module’s output attributes, bind the module call with let:
let network = use { source = './modules/network' }
let net = network {
cidr_block = '10.0.0.0/16'
subnet_cidr = '10.0.1.0/24'
az = 'ap-northeast-1a'
}
# Use module output to create another resource
awscc.ec2.RouteTable {
vpc_id = net.vpc_id
tags = { Name = 'my-route-table' }
}
The net.vpc_id reference accesses the vpc_id attribute declared in the module’s attributes block.
Splitting a module across multiple files
Every .crn file in a module directory is merged as a peer, so you can split a module across files however you like. A common convention is:
modules/network/
main.crn # resources
arguments.crn # arguments block
attributes.crn # attributes block
The filenames are arbitrary — main.crn is a convention, not a requirement. Load the module by pointing use at the directory:
let network = use { source = './modules/network' }
Nested modules
A module can load other modules. This lets you compose infrastructure from smaller building blocks.
For example, modules/network_with_rt/main.crn can load the network module:
let network = use { source = '../network' }
arguments {
cidr_block : String
subnet_cidr: String
az : String
}
let net = network {
cidr_block = cidr_block
subnet_cidr = subnet_cidr
az = az
}
let rt = awscc.ec2.RouteTable {
vpc_id = net.vpc_id
tags = {
Name = 'nested-module-rt'
}
}
attributes {
vpc_id : awscc.ec2.Vpc = net.vpc_id
route_table_id: awscc.ec2.RouteTable = rt.route_table_id
}
source paths are relative to the module file’s location.
Using modules with for expressions
Modules can be called inside for expressions to create multiple instances:
let vpc_mod = use { source = './modules/vpc_only' }
let cidrs = {
dev = '10.0.0.0/16'
stg = '10.1.0.0/16'
}
let networks = for name, cidr in cidrs {
vpc_mod {
cidr_block = cidr
env_name = name
}
}
This creates one VPC per entry in the cidrs map. See the For / If Expressions guide for more details.
Inspecting modules
Use the CLI to inspect a module’s structure:
# Show module arguments, attributes, and dependencies
carina module info modules/network
# List all modules loaded in a configuration
carina module list