estate_core — version 2.4

Source: presets/estate-core.satz

Purpose

The questions every estate has to answer on day 0, with the params they answer.

satz init writes these params — from its flags where it has one, from its defaults otherwise; an INTERVIEW asks for them one at a time, from the questions in this pack. Every param below has a question, and every question says what changing the answer later costs, so a default is offered where one exists and a value is required where none is possible.

Two kinds of param, and the interview treats them differently. Seven have NO POSSIBLE DEFAULT — the directory customer id, the organisation id, the domain, the short name, the display name, the first admin, the billing account: the pack cannot know the answer, so these block until a value is typed. The rest have a DERIVED OR CONVENTIONAL default — project and bucket names built from the short name, the folder name, the service account names, the region — and accepting one is an answer, recorded by writing it. A derived default is offered only once what it derives from is answered.

Which packs make up the estate — the security-group model included — is the next pack, estate-map.satz: this one is the day-0 params and nothing else.

This pack declares no resource. It publishes the day-0 answers every project beside the estate needs as the estate's core exports — values of every interface under interfaces/, which a project's own HCL or Satz reads them from — so an estate has them without writing an export of its own. Apart from those outputs, an estate that uses it and answers everything compiles to exactly what satz init would have written.

workload_folder_name is the folder where the customer's and the projects' folders live, named like infra_folder_name. It may be empty — the default — which is the organisation itself, and nothing is created for it; its question says so (empty), so "" is an answer the interview offers and the gate counts. Its export, workload_folder, is not here: it is the organisation, a static value, or a folder the estate declares, whose number is looked up, so the estate carries that section — satz init writes it from --workload-folder-name, an interview when the question is answered. The folder's parent is where its block stands in the estate, like any folder's.

Use it

params {
  // the pack's own defaults — copy only the lines you change
  customer_id = ""
  customer_organization_id = ""
  customer_domain = ""
  customer_shortname = ""
  first_admin = ""
  billing_account_infra = ""
  customer_longname = ""
  infra_folder_name = "Infrastructure"
  infra_project_name = "{customer_shortname}-infra-001"
  infra_bucket_name = "{customer_shortname}-infra-001-state"
  svc_iac_account = "svc-iac-001"
  svc_iac_users_group = "svc-iac-users"
  deployment_engine = "tofu"
  deployment_mode = "local"
  default_region = "europe-west3"
  default_zone = "{default_region}-a"
  workload_folder_name = ""
  compliance_frameworks = [ "cis-gcp-5.0" ]
}

use "presets/estate-core.satz"

Params

param default
customer_id ""
customer_organization_id ""
customer_domain ""
customer_shortname ""
first_admin ""
billing_account_infra ""
customer_longname ""
infra_folder_name "Infrastructure"
infra_project_name "{customer_shortname}-infra-001"
infra_bucket_name "{customer_shortname}-infra-001-state"
svc_iac_account "svc-iac-001"
svc_iac_users_group "svc-iac-users"
deployment_engine "tofu"
deployment_mode "local"
default_region "europe-west3"
default_zone "{default_region}-a"
workload_folder_name ""
compliance_frameworks [ "cis-gcp-5.0" ]

Questions

What to ask before this pack is configured, and what changing the answer costs.

asks about prompt reversal blast recommends
customer_id The Google Workspace / Cloud Identity directory customer id (C0…) recreate high —
customer_organization_id The organisation id (the number, not the domain) recreate high —
customer_domain The customer's primary domain recreate high —
customer_shortname A short name for this customer — lowercase, no spaces recreate high —
customer_longname The customer's display name, as it should appear in descriptions edit none —
first_admin The first admin's account, local part only (the domain is added) edit low —
billing_account_infra The billing account for the infrastructure project (XXXXXX-XXXXXX-XXXXXX) state_surgery low —
infra_folder_name Display name of the folder that holds the infrastructure project edit none "Infrastructure"
workload_folder_name Display name of the folder that holds the customer's and the projects' folders state_surgery high —
infra_project_name The infrastructure project id recreate high —
infra_bucket_name The state bucket name (globally unique) recreate high —
svc_iac_account Account id of the IaC service account (the part before @) recreate high —
svc_iac_users_group The group whose members may impersonate the IaC service account recreate low —
deployment_engine Which tool applies the emitted HCL: tofu or terraform edit none "tofu"
deployment_mode Where the state lives: local while bootstrapping, cloud once the bucket exists state_surgery low "local"
default_region The default region for regional resources recreate high "europe-west3"
default_zone The default zone, inside that region edit low —
compliance_frameworks Which compliance frameworks is this customer held to, as catalog ids: cis-gcp-4.0, cis-gcp-5.0, iso27001-2022 edit none [ "cis-gcp-5.0" ]

customer_id — Every group in the estate is created under this customer. There is no changing it afterwards; a different id is a different estate.

customer_organization_id — Every org policy, org grant and sink is scoped to it. A different organisation is a different estate.

customer_domain — Group addresses and the first admin are composed from it, and the Essential Contacts constraint allows it. Changing it renames every group.

customer_shortname — Project ids, the state bucket and group prefixes derive from it, and those ids are globally unique and immutable once created.

first_admin — This is the human satz bootstraps as, and the first member of the IaC users group — the one account that can act before any service account exists.

billing_account_infra — Relinking a project's billing is an update, but a project with no valid billing account cannot enable a single API — the bootstrap stops there.

workload_folder_name — Every project's folder takes it as its parent, through the core export workload_folder. Empty is the organisation itself, and nothing is created. A name declares the folder directly under the organisation; a folder the customer already has by that name is imported with satz adopt, because Google refuses a second one. Moving between the two later moves every folder the projects created under it.

An empty answer, "", is an answer: the organisation itself; no folder is created.

infra_project_name — A project id is immutable. Changing it creates a second project and abandons the first, with the state bucket inside it.

infra_bucket_name — The bucket holds the Terraform state for the whole estate. Renaming it means migrating state by hand and re-initialising every checkout.

svc_iac_account — Everything after init runs as this account and every provider block impersonates it. A different name is a new account, new grants, and a re-initialised backend.

svc_iac_users_group — Membership of this group is what lets a human operate the estate. Renaming it moves every member.

deployment_mode — satz migrate --mode cloud moves it and re-initialises the backend; starting in cloud mode needs a bucket that does not exist yet.

default_region — Regional resources — the alerting bucket, sinks with a location — cannot move regions; a change recreates them.

compliance_frameworks — This is what the customer answers to — a contract, an auditor, a regulator — and it is not the same as what the packs in this estate claim. satz report-compliance <estate> writes one report section per framework named here, satz prowler scans for them, and an audit pack reads them. cis-gcp-4.0 and cis-gcp-5.0 are the two versions of the CIS Google Cloud Foundation Benchmark; 5.0 is the current one and the answer unless a signed contract names 4.0. iso27001-2022 is ISO/IEC 27001:2022, covered as a cross-walk: the packs keep claiming CIS, every technically evidenced Annex A control reads its verdict through the CIS controls that stand as its evidence, and the controls that are a human duty carry that duty and never read as satisfied by configuration alone. Naming a framework here does not switch a pack on; it decides what the reports are written against. A value that names no catalog is refused.

Contributes

No resources — params and claims only.

Exports

Outputs of the root module and values of the interfaces under interfaces/ in every estate that uses this pack: a core export is carried by every interface, another one by its own interface and every interface that uses it. An interface a pack declares is common: every project's folder carries it.

interface output value description
core organization_id customer_organization_id The organisation id
core customer_domain customer_domain The customer's primary domain
core customer_shortname customer_shortname The short name project ids, bucket names and group prefixes derive from
core default_region default_region The default region for regional resources
core infra_project_id infra_project_name The infrastructure project, which holds the state bucket
core iac_service_account "{svc_iac_account}@{infra_project_name}.iam.gserviceaccount.com" The service account the estate is applied as

Claims

None — this pack proves no control by itself.

History

version date change
2.4 2026-09-26 the header, the workload_folder_name question and the section comments say project where they said team, and name the interfaces under interfaces/; params and exports are unchanged
2.3 2026-09-25 workload_folder_name, the folder where the customer's and the teams' folders live, default "" — the organisation, for which nothing is created — with its question, whose empty says so, so "" is an answer. The estate publishes it as workload_folder in the section satz init or an interview writes. An estate that uses the pack answers the question before bootstrap or an apply
2.2 2026-09-25 the core exports: organization_id, customer_domain, customer_shortname, default_region, infra_project_id and iac_service_account, each a core export — an output of the root module and of every module under hcl/interfaces/ — all known at compile time. An estate that uses the pack gains outputs.tf and hcl/interfaces/; its resources do not change
2.1 2026-09-21 compliance_frameworks, the catalogs this customer is HELD TO — a contract, an auditor, a regulator — as a list of catalog ids, with the question that asks for them. What an estate CLAIMS comes from its packs and is a different fact: an estate can claim CIS controls while its customer is audited against ISO 27001. The default is ["cis-gcp-5.0"]; the values are the ids of the catalogs in presets/catalogs/ (cis-gcp-4.0, cis-gcp-5.0, iso27001-2022) and a value that names no catalog is refused by the compile, with the list. satz report-compliance <estate> reports one section per framework named here, satz prowler scans for them beside the frameworks the packs claim, and a pack reads the param like any other. An estate that binds nothing keeps working: report-compliance <framework> <estate> is unchanged
2.0 2026-09-10 the security-model choice moves to estate_map; this pack is the seventeen day-0 params and their questions, nothing else. A major bump because two params left — no estate in the fleet uses the pack, it exists for interview skeletons
1.0 2026-09-09 first version: the seventeen day-0 params satz init writes, each with its question — what to ask, why, and what changing it later costs — plus the security-group model as two booleans and a question oneof. Emits nothing; exists so an interview (satz interview --create, the MCP tool satz_interview) has something to ask before an estate exists. Seven params have no possible default and block until typed; the rest offer one, and a derived default ("{customer_shortname}-infra-001") is offered only once its inputs are answered

The whole library's history: the changelog in presets/CHANGELOG.md.

Notes

The skeleton always uses this pack, ahead of estate_map: these are the values every later question assumes. Seven of the sixteen have no possible default and block until typed; the rest offer one, and a derived default appears only once what it derives from is answered — which is why an interview asks them in this order rather than all at once.

It emits nothing. satz init writes the params from its flags where it has one and from its defaults otherwise; satz interview --create and the MCP tool ask for them instead.