Skip to content
Spinbox is experimental: APIs change and data may be lost. Status lists what is built and what proves it.

modules/controlplane

View Markdown

Source: github.com/spin-stack/spin-terraform-aws//modules/controlplane?ref=<tag>. Generated from its variables.tf and outputs.tf.

VariableTypeDefault
spin_versionstringrequired
domainstringrequired
identity_record_daysnumber400
collectorobject({ metric_interval = optional(string, "60s") }){}
namestring"spin"
internal_zonestring"spin.internal"
acme_emailstring""
vpc_cidrstring"10.42.0.0/16"
availability_zonesnumber3
instance_typestring"t8i.medium"
root_volume_gbnumber40
admin_emailstring""
flow_logsbooltrue
dns_query_logsboolfalse
log_retention_daysnumber30
proxy_allowed_cidrslist(string)["0.0.0.0/0"]
proxy_instance_typestring"t8i.micro"
decommissionboolfalse
object_lock_daysnumber30
database_lock_daysnumber14
runner_policyobject({ shutdown_grace = optional(string, "100s") preemption_source = optional(string, "aws") }){}
installation_configstring""
runner_idle_minutesnumber60
quiet_hoursstring""
time_zonestring"UTC"
tagsmap(string){}
image_idsmap(string){}
monthly_budget_usdnumberthe module’s own
budget_emailslist(string)[]
image_measurementsmap(list(object({ pcr4 = string pcr7 = string pcr12 = string statement = optional(string) bundle = optional(string) # Why the build is not to be run, as measurements.json says of a revoked one: no machine of it # is trusted, and a release whose newest build is revoked is not installed (attested_key.tf). revoked = optional(string) })))the module’s own

The release the installation runs, as v<YYYYMMDD>.<N>: every machine boots the Spin OS image of it (spin-stack/ami), published into this account and tagged spin:version.

  • Type: string
  • Required

The base domain: the dashboard is app.<domain>, workspaces are *.ws.<domain>.

  • Type: string
  • Required

How many days the record of each identity a host signs is kept (identity/issued/ in the volumes bucket): longer than the bucket’s lock, and long enough to answer who took an identity when.

  • Type: number
  • Default: 400

The collector: how often metrics are pushed to it. Which Alloy it is, is the image’s.

  • Type: object({ metric_interval = optional(string, "60s") })
  • Default: {}

The installation’s name, one per region of an account: every resource’s prefix, the SSM path (/spin/<name>/…) each machine’s document is at, and - with the region - every IAM name. Two installations of one account may share a name in two regions, and not in one (claim.tf).

  • Type: string
  • Default: "spin"

The private zone the components reach each other by: cp.<this> and proxy.<this>, resolved only inside the VPC.

  • Type: string
  • Default: "spin.internal"

The address Let’s Encrypt writes to; empty is admin@<domain>.

  • Type: string
  • Default: ""

The VPC’s range, a /16. One /20 public subnet per availability zone is carved from its first half, and one /24 for the proxy per zone from x.x.136.0 on.

  • Type: string
  • Default: "10.42.0.0/16"

How many zones to make subnets in. The control plane, the proxy and the runners may each be started in any of them, which is what gives a group somewhere to go when a zone runs out.

  • Type: number
  • Default: 3

The control plane’s machine: the control plane, its database (PostgreSQL), the collector, and the stores of logs, traces and metrics it runs.

  • Type: string
  • Default: "t8i.medium"

The control plane’s disk: beside the read-only OS, /var takes the rest - the database, the logs’ and metrics’ stores and their caches. A new size is a new machine, replaced beside the old.

  • Type: number
  • Default: 40

Who the first administrator signs in as; empty is admin@<domain>. The one-time password is in the admin_password_parameter output’s parameter.

  • Type: string
  • Default: ""

Keep the VPC’s flow log in CloudWatch: every connection a security group accepted or refused. At a few hosts it is cents a month; a fleet whose workspaces move a lot of data pays about $0.50 a GB of flow records.

  • Type: bool
  • Default: true

Keep the VPC resolver’s query log in CloudWatch: every name looked up, a workspace’s included. It is Route 53 Resolver’s, and off unless asked for. Each installation that turns it on takes one of the ten CloudWatch Logs resource policies a region of an account may have.

  • Type: bool
  • Default: false

How long the flow and query logs are kept.

  • Type: number
  • Default: 30

Where users may reach the proxy on 443 from: the dashboard, workspaces and SSH. Anywhere by default; an office’s or a VPN’s ranges close the installation to everyone else. The runners reach it from inside the VPC whatever this says, and 80 stays open for the ACME challenge.

  • Type: list(string)
  • Default: ["0.0.0.0/0"]

The proxy’s machine: Caddy and nothing else, the one address the internet reaches.

  • Type: string
  • Default: "t8i.micro"

Set to true, and applied, before destroying the installation: its buckets lose the policies that refuse every object write from outside the VPC - the destroy runs from outside it - and a destroy empties every bucket, data included, bypassing the GOVERNANCE retention once the legal holds are lifted (README.md, “Removing an installation”). Never set on an installation that is to keep its data.

  • Type: bool
  • Default: false

The bucket’s default Object Lock retention, in GOVERNANCE mode. A deleted object’s bytes last this long, and the bucket’s lifecycle (bucket.tf) removes them a day after.

  • Type: number
  • Default: 30

The database archive’s default Object Lock retention, in GOVERNANCE mode: how long what a delete or an overwrite left there can still be restored. The lifecycle removes it a day after (database_bucket.tf).

  • Type: number
  • Default: 14

The host policy a runner starts with when it joins: how long it stays up once told to stop - a little less than the two minutes a spot reclaim gives - and the cloud that announces a reclaim.

  • Type: object({ shutdown_grace = optional(string, "100s") preemption_source = optional(string, "aws") })
  • Default: {}

The installation’s config file (spin’s configs/spin-example.yaml), as YAML. This module adds what it knows - the domain, who joins as a host, the autoscaling settings below - and writes it where the control plane reads it at every start, which seeds the database from it before it serves. What the file later says differently is shown in the dashboard to apply or dismiss, and nothing is written over what an administrator decided.

  • Type: string
  • Default: ""

How long the fleet has no workspace running, starting or waiting before the runners’ group is emptied.

  • Type: number
  • Default: 60

HH:MM-HH:MM in time_zone when an idle fleet is emptied at once rather than after runner_idle_minutes. A workspace asked for then still brings a runner up. Empty is none.

  • Type: string
  • Default: ""

The IANA zone quiet_hours are in.

  • Type: string
  • Default: "UTC"

Tags on everything this module creates.

  • Type: map(string)
  • Default: {}

Spin OS AMIs of your own, one per role - control-plane, runner and proxy - instead of those images.json names for spin_version in this region. Each must carry that release, laid for that role: a machine refuses a document of another release, and an image laid for another role.

  • Type: map(string)
  • Default: {}

What the installation may cost in a month, in US dollars: an AWS Budget on its spin:installation tag (budget.tf), which the control plane reads and alerts reach. Null is no budget.

  • Type: number
  • Default: null (the module’s own default)

Who AWS emails when the month’s cost passes 80 % or 100 % of monthly_budget_usd, or is forecast to pass it.

  • Type: list(string)
  • Default: []

The builds of each role’s image - control-plane, runner and proxy - oldest first, each the PCRs an instance of it measures (its manifest’s measurements, SHA384 in lowercase hex), instead of measurements.json’s: the control plane’s key is answered to the control plane’s builds (attested_key.tf), a host joins only as the runner’s, and the proxy is given its token only as the proxy’s (host_attestation). The control plane’s and the runner’s also carry statement and bundle, base64, as spin-os images sign writes them: what their images’ publisher signed of those PCRs. The last of a role’s is the image image_ids names. Required with image_ids, and for a release published before its PCRs were.

  • Type: map(list(object({ pcr4 = string pcr7 = string pcr12 = string statement = optional(string) bundle = optional(string) # Why the build is not to be run, as measurements.json says of a revoked one: no machine of it # is trusted, and a release whose newest build is revoked is not installed (attested_key.tf). revoked = optional(string) })))
  • Default: null (the module’s own default)
OutputWhat it is
nameThe installation’s name, claimed in its region (claim.tf), which the runners’ group and security group are named for.
iam_nameWhat the installation’s IAM names begin with: its name and its region, since IAM is the account’s and two regions may each have an installation of one name. The runners’ role and instance profile are named for it.
vpc_idThe VPC the runners go in.
subnet_idsOne public subnet per zone, for the runners’ group.
security_group_idThe control plane’s group; the runners module opens 8080 on it to its own.
session_manager_policy_arnSession Manager into a machine, and nothing else of SSM: what the runners’ role is given in place of AmazonSSMManagedInstanceCore, which reads every parameter.
urlThe control plane as runners reach it, the address its certificate names.
ca_certThe installation’s CA certificate, which every component trusts the control plane by.
runner_document_arnThe runners’ document, the one object of the documents’ bucket the runners’ role reads.
runner_user_dataA runner’s user data: its role and the runners’ document, as the systemd credentials Spin OS’s spin-boot takes.
imagesThe Spin OS images of the installation’s release, by role - the control plane’s, the runners’ and the proxy’s: each one’s id and root device. The runners module boots the runner’s.
boot_log_groupThe CloudWatch log group every machine’s boot writes to, a stream per <role>/<step>/<instance-id>: a role’s machines are one stream prefix.
boot_log_policy_arnWriting a machine’s boot to that group, and nothing else: the runners module attaches it to the runners’ role.
standard_vcpusThe on-demand standard vCPUs the control plane’s and the proxy’s machines take at most: two of each while an update replaces one. The runners module counts them against the quota on-demand runners share.
name_serversThe domain’s public zone’s nameservers, which its registrar points at.
domainThe base domain; the runners’ relay is tunnel.app.<domain>, dialled at relay_dial.
public_zone_idThe domain’s public zone, where modules/identity-issuer puts id.<domain>.
identity_key_arnThe KMS key the installation’s identity key is sealed under, which only an attested runner opens.
identity_documentsThe commands that write the identity issuer’s documents - discovery, key set, allowed signers and SPIFFE bundle - for modules/identity-issuer to publish: the identity key as the control plane keeps it in its bucket (its public half is all that is read) and the X.509 CA the first host to sign an SVID made beside it, then the documents from them. Run them with credentials that may read both, and commit what they write.
internal_zone_idThe private zone the components reach each other by, cp.<internal_zone> and proxy.<internal_zone>.
relay_dialWhere a runner dials the relay, host:port: the proxy inside the VPC, by the name the proxy of the moment points at itself.
collectorWhere every process of the installation pushes its telemetry, host:port. Where the collector sends it is set in the dashboard.
boundary_arnThe permissions boundary every role of the installation carries; the runners module puts it on the runners’ role.
proxy_ipThe proxy’s elastic IP, where app.<domain>, tunnel.app.<domain> and *.ws.<domain> point: the one address of the installation the internet reaches.
proxy_groupThe proxy’s group of one. Its machine, for aws ssm start-session --target (it has no SSH): aws autoscaling describe-auto-scaling-groups —auto-scaling-group-names <this> —query ‘AutoScalingGroups[0].Instances[0].InstanceId’.
bucketThe volumes’ bucket: every workspace’s disk, versioned under Object Lock, reached only through the VPC’s endpoint.
database_bucketThe database’s archive: the base backups and WAL the control plane ships as it writes, and restores from when its machine is replaced. Its role alone reaches it.
controlplane_groupThe control plane’s group of one. aws autoscaling start-instance-refresh --auto-scaling-group-name &lt;this> replaces its machine beside itself; its machine is reached with aws ssm start-session, as the proxy’s is.
admin_emailWho the first administrator signs in as.
admin_password_parameterThe SSM SecureString holding the first administrator’s one-time password: aws ssm get-parameter —with-decryption —name <this>. The first sign-in asks for a new one.
regionThe region everything here is in, read from the provider rather than asked for.
runner_idle_minutesHow long the fleet is idle before the runners’ group is emptied, which is why a first workspace waits for a machine’s boot.

Generated by task reference from spin-terraform-aws/modules/controlplane/variables.tf at 1557e9668.

Documents Spinbox v20261019.02. This page as Markdown, for a model or a terminal.