# The installation module

**Spinbox is experimental: APIs change and data may be lost.** What is built and what proves it is on [Status](https://spin-stack.github.io/website/reference/status.md); what Spinbox does not do is on [Limitations](https://spin-stack.github.io/website/overview/limitations.md).

> spin-terraform-aws, the module an installation uses: every variable it takes and every output it gives.

<!-- Generated by tools/refgen from spin-terraform-aws:variables.tf. Edit the source, not this file. -->

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

## Variables

| Variable | Type | Default |
|---|---|---|
| [`spin_version`](#spin_version) | `string` | **required** |
| [`domain`](#domain) | `string` | **required** |
| [`name`](#name) | `string` | the module's own |
| [`acme_email`](#acme_email) | `string` | the module's own |
| [`admin_email`](#admin_email) | `string` | the module's own |
| [`runner_policy`](#runner_policy) | `object({ shutdown_grace = optional(string) preemption_source = optional(string) })` | the module's own |
| [`proxy_allowed_cidrs`](#proxy_allowed_cidrs) | `list(string)` | the module's own |
| [`instance_type`](#instance_type) | `string` | the module's own |
| [`proxy_instance_type`](#proxy_instance_type) | `string` | the module's own |
| [`collector`](#collector) | `object({ metric_interval = optional(string) })` | the module's own |
| [`controlplane_volume_gb`](#controlplane_volume_gb) | `number` | the module's own |
| [`image_ids`](#image_ids) | `map(string)` | the module's own |
| [`image_measurements`](#image_measurements) | `map(list(object({ pcr4 = string pcr7 = string pcr12 = string statement = optional(string) bundle = optional(string) revoked = optional(string) })))` | the module's own |
| [`installation_config`](#installation_config) | `string` | the module's own |
| [`runner_idle_minutes`](#runner_idle_minutes) | `number` | the module's own |
| [`quiet_hours`](#quiet_hours) | `string` | the module's own |
| [`time_zone`](#time_zone) | `string` | the module's own |
| [`runner_instance_types`](#runner_instance_types) | `list(string)` | the module's own |
| [`runner_spot`](#runner_spot) | `bool` | the module's own |
| [`runner_data_volume_gb`](#runner_data_volume_gb) | `number` | the module's own |
| [`runner_root_volume_gb`](#runner_root_volume_gb) | `number` | the module's own |
| [`request_quota`](#request_quota) | `bool` | the module's own |
| [`max_runners`](#max_runners) | `number` | the module's own |
| [`runner_rollout`](#runner_rollout) | `string` | the module's own |
| [`decommission`](#decommission) | `bool` | the module's own |
| [`monthly_budget_usd`](#monthly_budget_usd) | `number` | the module's own |
| [`budget_emails`](#budget_emails) | `list(string)` | the module's own |
| [`tags`](#tags) | `map(string)` | the module's own |

### spin_version

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

- Type: `string`
- **Required**

### domain

The base domain: the dashboard is app.&lt;domain>, workspaces are \*.ws.&lt;domain>.

- Type: `string`
- **Required**

### name

The installation's name, one per region of an account (two regions may share it): every resource's prefix, the SSM path /spin/&lt;name>/, and with the region every IAM name. 2 to 26 characters; modules/controlplane's default is spin.

- Type: `string`
- Default: `null` (the module's own default)

### acme_email

The address Let's Encrypt writes to; none is admin@&lt;domain>.

- Type: `string`
- Default: `null` (the module's own default)

### admin_email

Who the first administrator signs in as; none is admin@&lt;domain>.

- Type: `string`
- Default: `null` (the module's own default)

### runner_policy

The host policy a runner starts with when it joins: shutdown_grace and preemption_source; modules/controlplane's default is a spot runner on AWS with 100s.

- Type: `object({ shutdown_grace = optional(string) preemption_source = optional(string) })`
- Default: `null` (the module's own default)

### proxy_allowed_cidrs

Where users reach the proxy on 443 from; modules/controlplane's default is anywhere.

- Type: `list(string)`
- Default: `null` (the module's own default)

### instance_type

The control plane's machine; modules/controlplane's default is t8i.medium.

- Type: `string`
- Default: `null` (the module's own default)

### proxy_instance_type

The proxy's machine; modules/controlplane's default is t8i.micro.

- Type: `string`
- Default: `null` (the module's own default)

### collector

The collector, as modules/controlplane's collector object: how often metrics are pushed to it; modules/controlplane's default is 60s. Where it sends is set in the dashboard.

- Type: `object({ metric_interval = optional(string) })`
- Default: `null` (the module's own default)

### controlplane_volume_gb

The control plane's disk, which /var - the database and the stores - takes all of past the OS; modules/controlplane's default is 40.

- Type: `number`
- Default: `null` (the module's own default)

### image_ids

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.

- Type: `map(string)`
- Default: `null` (the module's own default)

### image_measurements

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), instead of measurements.json's: the control plane's key is answered to the control plane's builds, a host joins only as the runner's, and the proxy is given its token only as the proxy's. The control plane's and the runner's also carry statement and bundle, base64, as spin-os images sign writes them. 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) revoked = optional(string) })))`
- Default: `null` (the module's own default)

### installation_config

The installation's config file (spin's configs/spin-example.yaml), as YAML: the control plane seeds its database from it at start, and shows a later change to it in the dashboard to apply or dismiss.

- Type: `string`
- Default: `null` (the module's own default)

### runner_idle_minutes

How long the fleet is idle before its runners' group is emptied; modules/controlplane's default is 60.

- Type: `number`
- Default: `null` (the module's own default)

### quiet_hours

HH:MM-HH:MM in time_zone when an idle fleet is emptied at once; none is none.

- Type: `string`
- Default: `null` (the module's own default)

### time_zone

The IANA zone quiet_hours are in; modules/controlplane's default is UTC.

- Type: `string`
- Default: `null` (the module's own default)

### runner_instance_types

What the runners' group may start, most preferred first, one CPU generation; see modules/runners.

- Type: `list(string)`
- Default: `null` (the module's own default)

### runner_spot

Every runner a spot instance; modules/runners' default is true.

- Type: `bool`
- Default: `null` (the module's own default)

### runner_data_volume_gb

An EBS volume for each runner's data, for instance types with no local NVMe (none of sa-east-1's with nested virtualization have one). modules/runners' default, 0, uses the instance store.

- Type: `number`
- Default: `null` (the module's own default)

### runner_root_volume_gb

Each runner's root disk: the OS and the machine's files. modules/runners' default is 30.

- Type: `number`
- Default: `null` (the module's own default)

### request_quota

Ask AWS for the vCPU quota max_runners runners need when the account's is lower; modules/runners' default is true.

- Type: `bool`
- Default: `null` (the module's own default)

### max_runners

The most runners the group may have; the control plane decides how many it has. modules/runners' default is 1.

- Type: `number`
- Default: `null` (the module's own default)

### runner_rollout

How a new release reaches the runners: idle, when the fleet next empties, moving no workspace (modules/runners' default); or rolling, replacing them at once, their workspaces suspended and resumed on the new hosts.

- Type: `string`
- Default: `null` (the module's own default)

### decommission

Set to true and apply before `tofu destroy`: the buckets lose their VPC-only policies and a destroy empties them, data included (README.md, "Removing an installation"). modules/controlplane's default is false.

- Type: `bool`
- Default: `null` (the module's own default)

### monthly_budget_usd

What the installation may cost in a month, in US dollars: an AWS Budget the control plane reads and whose alerts go to budget_emails. Null is no budget.

- Type: `number`
- Default: `null` (the module's own default)

### budget_emails

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

- Type: `list(string)`
- Default: `null` (the module's own default)

### tags

Tags on everything both modules create.

- Type: `map(string)`
- Default: `null` (the module's own default)

## Outputs

| Output | What it is |
|---|---|
| `next_steps` | What to do now that the apply is done, in order: DNS, the first sign-in, what the runners do, and how to reach a machine. Read it with `tofu output -raw next_steps`. |
| `dashboard` | The installation's dashboard. |
| `first_sign_in` | The setup page, the first administrator, and the command that reads their one-time password: an SSM SecureString this apply wrote without it ever being in the state. |
| `name_servers` | The domain's zone's nameservers: what its registrar, or its parent's zone, points at. |
| `proxy_ip` | The proxy's elastic IP, where app.&lt;domain>, tunnel.app.&lt;domain> and \*.ws.&lt;domain> point: the one address of the installation the internet reaches. |
| `controlplane_group` | The control plane's group of one; its machine is reached with `aws ssm start-session --target &lt;instance>`. |
| `update_status` | After an apply that changed a machine: how its replacement went. Successful, InProgress, or RollbackSuccessful with the reason - after which the old machine serves again, at once or, for a control plane that had handed its database over, once its watch has restored it from the archive. |
| `update_abort` | After an apply whose new machine will not come up: the command that stops waiting for it - rolls the refresh back and abandons the launch the hook still holds - so the next apply can change the group (task update:abort). |
| `boot_log` | By role, the command that reads what its machines' boots said - the ones that are gone included - and follows the next one's. |
| `runners_group` | The runners' group, which the control plane sizes: empty until a workspace waits for one. |
| `controlplane` | Everything modules/controlplane outputs. |
| `runners` | Everything modules/runners outputs. |
