Tagging Resources
Most companies have a tagging policy: an owner, a cost centre, an environment, on everything. The
usual way to satisfy it is for every module to accept a tags variable and every caller to pass the
same map, which means the policy is enforced by convention across every file that creates anything,
and a new module is compliant only if its author remembered.
Octo applies tags from outside the modules. You declare them once in octo.yaml, with a scope, and
every resource a run produces picks them up. Module authors write nothing.
The scopes
tags is a list, and each entry is a set of tags together with the scope they reach.
tags:
# Every resource in the run.
- tags:
ManagedBy: octo
Owner: platform-team
# Every resource the region module produced.
- scope:
moduleId: region-module
tags:
Tier: network
scope is optional, and leaving it out means the same as writing scope: {}: the tags reach
everything. A moduleId narrows them to the resources that one module declared, and where both set
the same key, the narrower scope wins.
A key belongs to one entry per scope. Two global entries that both set Owner stop the run before
anything is written:
Error: Tag has already been registered!
inputKey: Owner
A scope naming a module that is not in modules fails the same way, with Module not found! and
the ID you typed. Tag values are strings, so an unquoted true or a number is caught earlier still,
by the schema:
[/tags/1/tags/Critical] must be string
A value may come from env like any other, as "${env.COST_CENTRE}", which is how one file carries
a different cost centre per deployment.
Where the tags end up
Octo resolves the tags for every resource before the Terraform is written, so the two entries above reach the shop's VPC as one map:
resource "aws_vpc" "vpc-eu-west-1" {
provider = aws._123456789012-eu-west-1
cidr_block = "10.0.0.0/16"
enable_dns_hostnames = true
enable_dns_support = true
instance_tenancy = "default"
tags = {
ManagedBy = "octo"
Owner = "platform-team"
Tier = "network"
}
}
The receipts bucket sits in a different module, so it carries the two global tags and not Tier:
resource "aws_s3_bucket" "bucket-shop-receipts" {
provider = aws._123456789012-eu-west-1
bucket = "shop-receipts"
tags = {
ManagedBy = "octo"
Owner = "platform-team"
}
}
Every resource in @quadnix/octo-aws-cdk that Terraform creates carries them, so a tag you declare
reaches the whole stack. A resource of your own opts in with two lines, which Create a Terraform
Resource shows.
Tags are part of the diff, so adding one to a live stack shows up in the next report as an update on each resource that gains it, rather than as an invisible change.
What to tag with
Two things are worth declaring globally on any real project, and neither is about cost reporting.
An owner, so that whoever finds an unexpected resource in the console knows who to ask. And a marker saying the resource is managed by Octo, so that a resource created by hand is distinguishable from one this project is responsible for. The second one pays for itself the first time somebody asks whether a bucket is safe to delete.