The repository root¶
The root of a landing-zone repository holds meta: documentation, the pinned
toolchain, the conftest policy, and the ignore rules. Everything deployable lives
under live/. This page covers the root, minus live/ and .github/.
The root files¶
| File | Purpose | You edit it |
|---|---|---|
README.md |
What the repository is, how a change is made, the live/ layout |
Optional |
ONBOARDING.md |
Step-by-step for standing up a new customer landing zone | No |
CHANGELOG.md |
Per-customer record of version bumps and new workloads | Yes, on every version bump |
LICENSE |
Placeholder proprietary licence | Yes, before external distribution |
mise.toml |
Pinned Terragrunt and Terraform versions | Only to move the toolchain |
.editorconfig |
UTF-8, LF, two-space indent, trailing-whitespace rules | No |
.gitignore |
Keeps generated and local files out of git | Rarely |
projects.yml |
Engine discovery and gate hooks, covered on the next page | Yes |
mise.toml: the pinned toolchain¶
The repository pins the two tools that matter, and nothing else:
Run mise install in the repository root and you get exactly those versions
locally. CI runs the same pins, so a local terragrunt plan and a plan in a pull
request see the same binaries. To move the toolchain, change the versions here,
open a pull request, and review the plans.
.gitignore: what must never be committed¶
The ignore list is short, and two entries carry real weight.
backend.tf, providers.tf, and context.tf are generated. live/root.hcl
writes them into each unit at run time, from the shared backend, provider, and
context contract. They are build output, not source. If you commit one, that unit
stops taking the shared contract and starts carrying a stale copy, and the next
contract change silently skips it. A committed context.tf is the worst of the
three: the unit keeps deploying to the old region after region.hcl has moved.
.alzlib/ is the alz provider's local cache of the ALZ library it downloads at
the pinned library_references. It is a cache, it is rebuilt on demand, and it is
large.
The rest is routine: **/.terraform/, **/.terragrunt-cache/,
**/.terragrunt-stack/, local state files, **/*.local.hcl and
**/*.local.tfvars for local overrides, and editor directories.
LICENSE¶
The shipped LICENSE is a self-declared NRIT Solutions proprietary notice, and it
says so in its own text: the binding terms live in the partnership agreement.
Replace it with the agreed licence before the repository is distributed outside the
customer.
policy/governance.rego: the active policy¶
policy/ is where the conftest gate looks. The repository ships one file,
policy/governance.rego, in package main. It contains a single warn rule:
warn contains msg if {
resource := input.resource_changes[_]
"delete" in resource.change.actions
msg := sprintf("%s will be destroyed", [resource.address])
}
That surfaces every resource a plan would destroy, so a destructive plan is obvious
in review. It is a warn, so it reports and does not block. This is the file you
extend: add rules here as your tenant gets clean, and promote them from warn to
deny when you want them enforced. For when and in what order to enforce, see
Enforcement strategy.
examples/policy/tags.rego: reference only¶
examples/policy/tags.rego is not loaded by the gate. It sits outside policy/ on
purpose. Copy it into policy/ and adapt it when you want a first deny rule.
It exists mostly to document two plan-JSON patterns that are easy to get wrong:
change.actionsis an array. A replace is["delete","create"], an unchanged resource is["no-op"]. Test membership within, neveractions[0].change.afteris null on a destroy. Guard it, or the rule evaluates to undefined and thedenyfails open silently instead of firing.
Both bite the same way: the policy looks correct, the gate reports clean, and nothing was actually checked.
An unguarded deny fails open
Rego returns undefined, not false, when it walks into a null. A deny that
reads change.after without a null guard produces no message on a destroy
plan, which reads as a pass. Guard change.after != null in every rule that
touches it.
The in-repo docs/ folder¶
The repository carries its own docs/ folder. These files ship inside the
customer's repository, so the operating team has the reference offline and next to
the code, without depending on this site.
| File | Covers |
|---|---|
upgrade-guide.md |
The three pinned things: AVM module versions, ALZ library refs, and the engine pins |
policy-gates.md |
The conftest, checkov, and infracost gates, and how to enforce them |
foundation-structure.md |
Why the foundation sits above the subscriptions, and how it behaves per region |
leaf-data-sharing.md |
dependencies versus dependency, and how to move values between units |
operations-runbook.md |
Teardown ordering, AMBA remediation, the change process, adding a workload, drift, and credential rotation |
The step-by-step for standing up a new customer is ONBOARDING.md at the
repository root, not in docs/. There is no separate onboarding checklist file.