> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://rivet.chrisboulton.com/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://rivet.chrisboulton.com/_mcp/server.

# Resource and module expansion

> Stable named keys, numbered slots, and compile-time iteration values.

Resources and modules accept either `for_each` or `count` at the declaration level, outside `meta`. They are mutually exclusive. Expansion occurs during compilation and produces ordinary resources and module instances in the bundle.

## Named instances

```hcl
variable "files" {
  type = map(string)
  default = {
    first  = "/etc/rivet-first"
    second = "/etc/rivet-second"
  }
}

file "messages" {
  for_each = var.files
  path     = each.value
  content  = "instance=${each.key}\n"
}

output "first" {
  type  = resource("file")
  value = file.messages["first"]
}
```

`for_each` accepts a map or a set of strings. Map keys or set values establish identity; `each.key` and `each.value` belong to that instance. Keys must be nonempty strings without control characters. Lists are not implicit sets: use `toset(["first", "second"])` when appropriate.

Sensitive identities fail without entering addresses or diagnostics. Public map keys may accompany sensitive values. Marking an entire variable sensitive taints its keys too, so it cannot provide public expansion identity.

## Numbered slots

```hcl
variable "enabled" {
  type    = bool
  default = true
}

file "slots" {
  count   = var.enabled ? 2 : 0
  path    = "/etc/rivet-slot-${count.index}"
  content = "slot=${count.index}\n"
}
```

`count` requires a public, nonnegative compile-time integer, including an integer fact, and exposes `count.index`. Named keys render with quotes (`file.messages["first"]`); numeric slots render without them (`file.slots[0]`). String and numeric keys are distinct serialized identities. Indexed references must use the corresponding key type.

Zero count and empty collections create no instances. Compilation permits at most 10000 resources and 1024 module instances; each expansion is bounded and nested modules share the global limits.

## Module families

Use the file module from [modules](/guides/modules):

```hcl
module "files" {
  source   = "./modules/file"
  for_each = { first = "/etc/module-first", second = "/etc/module-second" }
  path     = each.value
  text     = "instance=${each.key}\n"
}

module "slots" {
  source = "./modules/file"
  count  = 2
  path   = "/etc/module-slot-${count.index}"
  text   = "slot=${count.index}\n"
  meta { depends_on = [module.files] }
}

output "first_file" {
  type  = resource("file")
  value = module.files["first"].handle
}
```

Whole expanded module families can be used in ordering metadata; Rivet expands them to instance boundaries. Individual outputs use an indexed module instance.

## Identity changes and removal

Reordering named keys preserves addresses and catalog digests. Reordering a list selected by count can assign an existing slot to a different physical object. Changing a key creates a new declaration.

Removing a key or reducing count does not delete host objects or undo earlier side effects. Obsolete journal work is superseded on the next real apply; declare explicit absent resources when host-object removal is intended. See [recovery](/reference/recovery).