ADR-0006: AWS Resource Tagging Taxonomy
Decision to establish a mandatory three-tier tagging taxonomy for all AWS resources to enable cost allocation and operational traceability.
| Field | Value |
|---|---|
| Status | Approved |
| Date | 2026-03-12 |
| Authors | Engineering |
| Replaces | — |
Context
Ontopix operates with a single AWS account hosting workloads across multiple products, client engagements, and internal initiatives. Without a shared tagging standard, AWS costs are undifferentiated: there is no reliable way to attribute spend to a product, a client, or a team; no way to reconcile infrastructure costs against client invoices; and no way to trace a running resource back to the code that created it.
This ADR establishes a mandatory tagging taxonomy that addresses two complementary concerns:
- Cost allocation — attributing AWS spend to the correct product, billing mode, client, and internal cost center.
- Operational traceability — identifying who owns a resource and what code manages it.
The taxonomy is designed to scale from the current single-account setup to a future multi-account AWS Organizations structure, where billing-mode and client map directly to account-level separation.
Decision
All AWS resources provisioned by Ontopix MUST carry a standard set of tags. Tags are organized in two tiers: Required (applied to every resource without exception) and Contextual (applied based on billing-mode). A third, optional tier exists for fine-grained operational signals.
Tags are defined in Terraform locals and injected via default_tags on the AWS provider. Manual exceptions must be documented and tagged before reaching production.
Tag Taxonomy
Tier 1 — Required
These tags MUST be present on every AWS resource.
| Tag key | Purpose | Values |
|---|---|---|
product | Ontopix product line that owns this resource | agents · audits · mistery · platform · shared |
billing-mode | Who bears the cost of this resource | client · saas · internal · rd |
env | Deployment environment | prod · pre · dev · sandbox |
team | Team accountable for this resource | engineering · marketing · operations · infra · finance |
owner | Email address of the accountable individual or team alias | platform@ontopix.ai · engineering@ontopix.ai |
source | Repository and path of the Terraform root module that created this resource | ontopix/infra/global · ontopix/mcp-services/examples/hello-world/.infra |
managed-by | Tool used to provision this resource | terraform · manual · cdk |
billing-mode semantics
| Value | Meaning |
|---|---|
client | Cost is attributable to a specific client and may be recovered through invoicing |
saas | Cost belongs to the shared SaaS platform, not a specific client |
internal | Ontopix internal tooling — not tied to any client or product line |
rd | Research and development; no current billing target |
env values
| Value | Meaning |
|---|---|
prod | Production — live, customer-facing |
pre | Pre-production / staging — mirrors production |
dev | Development — active development and integration testing |
sandbox | Isolated developer sandbox; ephemeral, non-shared |
source convention
Format: ontopix/{repository-name}/{path-to-root-module}
The path is the directory containing the Terraform root module (i.e. the directory with backend.tf). This is a static string — it is not derived from a variable. It is the only tag that requires a manual override when a repository has multiple root modules (e.g. per-environment .infra/ directories).
ontopix/infra/global
ontopix/schemas/.infra
ontopix/mcp-services/examples/hello-world/.infra
owner convention
Use team aliases for shared or long-lived infrastructure (platform@ontopix.ai, engineering@ontopix.ai). Use personal addresses only for short-lived or individually owned resources. Personal addresses must be updated when ownership changes.
Tier 2 — Contextual
These tags are required when billing-mode = client. They provide the detail needed to attribute costs to a specific client engagement and to produce accurate invoices.
| Tag key | Purpose | Values / format |
|---|---|---|
client | Client identifier | Lowercase single word or acronym (e.g. acme, retailco) |
project | Specific engagement within the client relationship | Short slug (e.g. support-agent-v1, audit-pilot) |
cost-center | Finance cost center ID for billing roll-up | Provided by Finance (see open questions) |
component | Architectural layer this resource belongs to | api · worker · db · infra · ml · frontend · edge |
Notes on Tier 2 values
client: No canonical client registry exists yet. Use lowercase, no hyphens, no spaces. A client registry will be established as a follow-on to this ADR.
project: Only used when billing-mode = client. Not used for saas, internal, or rd resources.
cost-center: The specific taxonomy and IDs will be defined by Finance. This tag MUST be set on all billing-mode = client resources; the value will be provided per engagement. Placeholder: tbd until Finance publishes the registry.
Tier 3 — Optional (Extended attributes)
Optional tags for fine-grained tracking. All Tier 3 tag keys use the prefix extra: to distinguish them from required tags and to prevent key collisions with future Tier 1/2 additions.
| Tag key | Purpose | Value format |
|---|---|---|
extra:feature | Specific product feature or architectural experiment | debounce · copilot |
extra:channel | Communication channel served by this resource | whatsapp · email · zendesk |
extra:data-class | Data sensitivity classification | pii · internal · public |
The extra: prefix convention means:
- Tier 3 tags are immediately recognizable in Cost Explorer filters and AWS Config.
- Adding a new Tier 3 tag never risks colliding with a future required tag name.
extra:*can be excluded or included as a group in IAM tag conditions and Config rules.
Implementation
Terraform — provider-level default tags
# .infra/providers.tf
provider "aws" {
region = var.aws_region
default_tags {
tags = local.tags
}
}
Terraform — locals pattern
# .infra/locals.tf
locals {
tags = {
# Tier 1 — always required
product = "agents"
billing-mode = "client"
env = var.environment
team = "engineering"
owner = "engineering@ontopix.ai"
source = "ontopix/my-repo/.infra"
managed-by = "terraform"
# Tier 2 — required because billing-mode = "client"
client = "acme"
project = "support-agent-v1"
cost-center = "tbd" # replace with Finance-issued ID
component = "worker"
# Tier 3 — optional
"extra:channel" = "channel-whatsapp"
}
}
Environment variable validation
# .infra/variables.tf
variable "environment" {
description = "Deployment environment"
type = string
validation {
condition = contains(["prod", "pre", "dev", "sandbox"], var.environment)
error_message = "environment must be one of: prod, pre, dev, sandbox"
}
}
AWS Config enforcement
A Config managed rule in the central infra repository enforces Tier 1 tags on all resources:
resource "aws_config_config_rule" "required_tags" {
name = "ontopix-required-tags"
source {
owner = "AWS"
source_identifier = "REQUIRED_TAGS"
}
input_parameters = jsonencode({
tag1Key = "product"
tag2Key = "billing-mode"
tag3Key = "env"
tag4Key = "team"
tag5Key = "owner"
tag6Key = "source"
# tag7Key = "managed-by" # enable after remediation window
})
}
Enforcement rollout
Enforcement follows a two-phase approach:
- Phase 1 — Alerting only (first 30 days): Enable the
REQUIRED_TAGSConfig rule in evaluation mode. Non-compliant resources trigger alerts routed to the resourceowner(when available) and toinfra@ontopix.aias a catch-all. Alert channels: email notification and, where feasible, a GitHub issue on the repository identified by thesourcetag. - Phase 2 — Block non-compliant creates: After the team has built the habit of reacting to alerts and existing resources have been remediated, enforce compliance via SCP to prevent creation of resources missing Tier 1 tags.
Alternatives Considered
Account-per-client isolation via AWS Organizations
Separate accounts per client provides hard billing and security boundaries. This is the correct long-term architecture and this taxonomy is explicitly designed to translate into it: billing-mode=client + client=X maps to a future client-X account. Rejected as the immediate solution due to operational overhead at current scale.
Tag keys in PascalCase (ManagedBy, Environment)
Consistent with some AWS-native tools and third-party FinOps platforms. Rejected because our Terraform and YAML conventions use kebab-case throughout, and mixed casing across tag keys breaks Cost Explorer filter composition silently (tag keys are case-sensitive in AWS).
No extra: prefix for optional tags
Simpler, but creates ambiguity between an established required tag and a locally adopted optional one. The prefix makes tier membership visible without consulting this document.
Free-form cost-center values
Letting teams define their own cost center slugs produces unsquashable inconsistency. Deferring to Finance for a formal registry is the right call even if it means a short tbd period.
Consequences
Positive
- Per-client, per-product, and per-team cost reports are immediately constructable in AWS Cost Explorer using Tier 1 tags.
sourceenables one-click navigation from any tagged AWS resource to the exact Terraform module that created it.ownerenables cost anomaly alerts and security findings to route to the right contact without manual lookup.- The taxonomy is future-proof: Tier 1 + Tier 2 tags map directly to AWS Organizations account structure when multi-account separation is adopted.
- AI agents provisioning infrastructure have an explicit, checkable contract.
Negative / Trade-offs
sourcecannot be set purely viadefault_tagsin repositories with multiple Terraform root modules — requires a per-module local override.cost-centerwill carrytbdvalues until Finance publishes the cost center registry. This limits invoice reconciliation accuracy in the short term.- Existing untagged resources require a one-time remediation pass before Config enforcement is enabled.
ownervalues on long-lived resources need active governance; team aliases are strongly preferred over personal addresses for shared infrastructure.billing-mode=rdcan accumulate spend silently if not reviewed regularly. Engineering leadership should reviewrd-tagged costs monthly.
Open Questions (RFC)
- Finance cost center registry — when will Finance define the
cost-centertaxonomy? Until then, what is the approved placeholder value? (tbd,unassigned, or per-engagement provisional slugs?) - Client registry —
clientvalues are currently informal (lowercase slug, no source of truth). Should a canonical client registry be maintained as part of this ADR, in a separate document, or deferred to a CRM integration? extra:tag governance — who approves newextra:tag keys to prevent proliferation? Engineering lead sign-off, or self-service with documentation required?
Resolved Questions
- Config enforcement rollout — alerting-only for 30 days, then block non-compliant resources via SCP. Alerts route to the resource
ownerand toinfra@ontopix.ai. Where feasible, create a GitHub issue on the repository identified by thesourcetag. - Multi-account transition —
billing-modeis retained even after adopting AWS Organizations. Not every client will warrant a dedicated account; only the larger engagements will. Keepingbilling-modeon all resources regardless of account provides consistent cost tracking across the entire estate.
Applies Principles
- Evidence Over Assumptions — tagged resources produce cost data; untagged resources require guesswork.
- Ownership & Responsibility —
ownerandteammake accountability visible in billing reports, security findings, and incident response. - Automation Over Manual Work —
default_tagson the provider means tagging is a one-time definition, not per-resource boilerplate. - Long-Term Thinking — the taxonomy translates directly to AWS Organizations account-level separation when Ontopix reaches that scale.
- Security by Design —
managed-by=manualis a drift signal;ownerroutes security findings to the right contact without manual lookup.
Related
- Pattern: AWS Resource Tagging — implementation guide for this decision
- Infrastructure Layout pattern — where
.infra/andlocals.tflive - Core Engineering Principles
ADR-0004: Infrastructure Layout
Decision to use .infra/ convention for infrastructure-as-code in application repositories.
ADR-0007: AWS Bedrock Inference via Application Inference Profiles
Decision to require application inference profiles for all AWS Bedrock model invocations to enable cost attribution and tagging.