Skip to content

Commit d7d5b9c

Browse files
docs(microvm): document Terraform compute provider
1 parent 7036f1b commit d7d5b9c

6 files changed

Lines changed: 104 additions & 10 deletions

File tree

docs/modules/internal/compute-provider-refactor.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
The scale-up, scale-down, pool, job-retry, queue, SSM housekeeping, and GitHub registration workflows are not inherently EC2-specific. The legacy `runners` module combines that common control plane with EC2 launch templates, instance profiles, bootstrap parameters, log groups, IAM permissions, and Lambda environment variables. Adding another compute provider in that structure would require copying common behavior or adding provider conditionals throughout the module.
1010

11-
The refactor introduces a provider boundary so a future MicroVM or other backend can reuse the control plane. Only the policy statements, environment variables, and resources required by the selected compute provider should change.
11+
The refactor introduces a provider boundary so MicroVM and other backends can reuse the control plane. Only the policy statements, environment variables, and resources required by the selected compute provider should change.
1212

1313
## Ownership model
1414

@@ -25,11 +25,11 @@ The implementation is split into orchestration, provider-neutral control-plane c
2525
| `compute-providers/<provider>/trust-policy` | Provider-specific default runner-role trust, merged with the optional caller-provided trust document before the common role is created. |
2626
| `compute-providers/<provider>` | Provider-specific resources, permission requirements, and the IAM and environment-variable fragments consumed by the common control plane after the runner role is resolved. |
2727

28-
The EC2 provider owns the instance profile, launch template, security group, AMI and bootstrap parameters, runner log groups, EC2 policy statements, and EC2 Lambda environment variables. EC2 is the only implemented Terraform compute provider in this phase.
28+
The EC2 provider owns the instance profile, launch template, security group, AMI and bootstrap parameters, runner log groups, EC2 policy statements, and EC2 Lambda environment variables. The MicroVM provider owns the Lambda MicroVM runtime configuration, execution-role policy, and MicroVM Lambda environment variables. Terraform does not manage MicroVM lifecycle resources directly; the runtime control plane creates and terminates MicroVM runners.
2929

3030
The modules below `runner-stack` are internal implementation boundaries, not standalone public modules. Callers opt into the experimental interface through `experimental.multi_runner_config_v2`; `multi-runner` calls `runner-stack`, which composes the internal modules. Their direct input and output contracts may change while v2 remains experimental.
3131

32-
`runner-stack` selects a compute provider from the single populated typed block under `compute_provider`. For example, `compute_provider = { ec2 = { ... } }` selects EC2; there is no separate `type` input that can disagree with the populated block. Exactly one provider block must be populated, and its presence must be known during planning because it determines the module graph. Native input validation enforces this common selection rule, while each compute-provider module owns its provider-specific semantic validation. The stack passes `compute_provider.<provider>` to the selected provider module as one nested `config` object. It also passes the provider-neutral `runner`, `github`, `ssm`, and `observability` objects without expanding them back into prefixed scalar inputs. This keeps ownership visible at the module boundary and gives future compute providers an equivalent contract to implement.
32+
`runner-stack` selects a compute provider from the single populated typed block under `compute_provider`. For example, `compute_provider = { ec2 = { ... } }` selects EC2 and `compute_provider = { microvm = { ... } }` selects MicroVM; there is no separate `type` input that can disagree with the populated block. Exactly one provider block must be populated, and its presence must be known during planning because it determines the module graph. Native input validation enforces this common selection rule, while each compute-provider module owns its provider-specific semantic validation. The stack passes `compute_provider.<provider>` to the selected provider module as one nested `config` object. It also passes the provider-neutral `runner`, `github`, `ssm`, and `observability` objects without expanding them back into prefixed scalar inputs. This keeps ownership visible at the module boundary and gives future compute providers an equivalent contract to implement.
3333

3434
The common stack creates or selects the runner IAM role, but the selected provider owns the role's default trust-policy document. Each provider implements a small `trust-policy` submodule that accepts `additional_trust_policy_json` and returns the final `assume_role_policy`. The full provider separately returns its nested `provider` contract containing `policies.runner`, `policies.scale_up`, `policies.scale_down`, and `policies.pool`, component environment variables, and provider resources. The common stack uses the isolated trust-policy output when it creates the runner role and attaches the full provider's permission documents to the roles owned by the corresponding common components. A provider never creates or attaches a common IAM role.
3535

@@ -123,7 +123,7 @@ Tags follow the same ownership model. Module tags are defaults; shared Lambda, q
123123

124124
Application logging settings stay together under `observability.logs`, including `level`, retention, encryption, class, and shared log-group tags.
125125

126-
In v1 mode, entries remain exclusively in `runners_map` and retain their flat output fields; `runners_map_v2` is empty. In v2 mode, entries are exposed exclusively through `runners_map_v2` and `runners_map` is empty. Common resources are grouped under `runner`, `scale_up`, `scale_down`, and `pool`, while provider-specific resources remain under `provider.<provider>`. The provider key is derived dynamically from the selected input block and therefore also identifies the compute provider. For example, the common runner role is available at `runners_map_v2["configuration"].runner.role`, while an EC2 selection places launch-template and runner-log artifacts under `runners_map_v2["configuration"].provider.ec2`. The `pool` value is null when no pool configuration is supplied.
126+
In v1 mode, entries remain exclusively in `runners_map` and retain their flat output fields; `runners_map_v2` is empty. In v2 mode, entries are exposed exclusively through `runners_map_v2` and `runners_map` is empty. Common resources are grouped under `runner`, `scale_up`, `scale_down`, and `pool`, while provider-specific resources remain under `provider.<provider>`. The provider key is derived dynamically from the selected input block and therefore also identifies the compute provider. For example, the common runner role is available at `runners_map_v2["configuration"].runner.role`, an EC2 selection places launch-template and runner-log artifacts under `runners_map_v2["configuration"].provider.ec2`, and a MicroVM selection places image and execution-role references under `runners_map_v2["configuration"].provider.microvm`. The `pool` value is null when no pool configuration is supplied.
127127

128128
## Plan-time provider selection and ownership wrappers
129129

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
<!-- BEGIN_TF_DOCS -->
2+
## Requirements
3+
4+
| Name | Version |
5+
|------|---------|
6+
| <a name="requirement_terraform"></a> [terraform](#requirement\_terraform) | >= 1.4.0 |
7+
| <a name="requirement_aws"></a> [aws](#requirement\_aws) | >= 6.33 |
8+
9+
## Providers
10+
11+
| Name | Version |
12+
|------|---------|
13+
| <a name="provider_aws"></a> [aws](#provider\_aws) | >= 6.33 |
14+
| <a name="provider_terraform"></a> [terraform](#provider\_terraform) | n/a |
15+
16+
## Modules
17+
18+
No modules.
19+
20+
## Resources
21+
22+
| Name | Type |
23+
|------|------|
24+
| [terraform_data.validate_config](https://registry.terraform.io/providers/hashicorp/terraform/latest/docs/resources/data) | resource |
25+
| [terraform_data.validate_runner](https://registry.terraform.io/providers/hashicorp/terraform/latest/docs/resources/data) | resource |
26+
| [aws_iam_policy_document.scale_down](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
27+
| [aws_iam_policy_document.scale_up](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
28+
29+
## Inputs
30+
31+
| Name | Description | Type | Default | Required |
32+
|------|-------------|------|---------|:--------:|
33+
| <a name="input_aws_partition"></a> [aws\_partition](#input\_aws\_partition) | AWS partition used to construct IAM ARNs. | `string` | `"aws"` | no |
34+
| <a name="input_aws_region"></a> [aws\_region](#input\_aws\_region) | AWS region used by compute-provider resources and policy documents. | `string` | n/a | yes |
35+
| <a name="input_config"></a> [config](#input\_config) | Lambda MicroVM compute-provider configuration. Paths match `compute_provider.microvm` in the runner stack.<br/><br/>- `image_identifier`: ARN or ID of the MicroVM image used to run GitHub runners.<br/>- `image_version`: Optional MicroVM image version.<br/>- `execution_role`: Optional externally managed execution role assumed by MicroVMs. Null uses the common runner role.<br/>- `execution_role.arn`: ARN of the externally managed MicroVM execution role.<br/>- `egress_network_connectors`: Egress network connectors passed to RunMicrovm.<br/>- `idle_policy`: Optional auto-suspend and auto-resume configuration passed to RunMicrovm.<br/>- `idle_policy.max_idle_duration_seconds`: Maximum idle time before MicroVM auto-suspend.<br/>- `idle_policy.suspended_duration_seconds`: Maximum suspended time before MicroVM termination.<br/>- `idle_policy.auto_resume_enabled`: Enables automatic resume on inbound traffic while suspended.<br/>- `logging`: Optional RunMicrovm logging union. Exactly one of `cloud_watch` or `disabled` must be selected when set.<br/>- `logging.cloud_watch.log_group`: Optional CloudWatch Logs log group used by MicroVM runtime logs.<br/>- `logging.cloud_watch.log_stream`: Optional CloudWatch Logs log stream used by MicroVM runtime logs.<br/>- `logging.disabled`: Disables MicroVM runtime logging when true.<br/>- `run_hook_payload`: Optional payload delivered to the MicroVM `/run` hook. Maximum 16,384 characters.<br/>- `maximum_duration_in_seconds`: Optional maximum MicroVM lifetime. Valid range is 1 through 28,800 seconds.<br/>- `environment_variables`: Additional provider-specific Lambda environment variables merged into scale-up, scale-down, and pool.<br/>- `tags`: Tags encoded into the MicroVM runner configuration.<br/>- `iam.resource_arns`: Resource ARNs used by the generated MicroVM control-plane policies. The service is new and some actions may require `*`.<br/>- `iam.actions.scale_up`: MicroVM IAM actions used by scale-up and pool.<br/>- `iam.actions.scale_down`: MicroVM IAM actions used by scale-down.<br/>- `iam.additional_policy_json.scale_up`: Optional additional provider policy attached separately to the scale-up Lambda role.<br/>- `iam.managed_policy_arns.scale_up`: Optional managed policy attached to the scale-up Lambda role.<br/>- `iam.managed_policy_arns.pool`: Optional managed policy attached to the pool Lambda role. | <pre>object({<br/> image_identifier = string<br/> image_version = optional(string, null)<br/> execution_role = optional(object({<br/> arn = string<br/> }), null)<br/> egress_network_connectors = optional(list(string), [])<br/> idle_policy = optional(object({<br/> max_idle_duration_seconds = number<br/> suspended_duration_seconds = number<br/> auto_resume_enabled = bool<br/> }), null)<br/> logging = optional(object({<br/> cloud_watch = optional(object({<br/> log_group = optional(string, null)<br/> log_stream = optional(string, null)<br/> }), null)<br/> disabled = optional(bool, false)<br/> }), null)<br/> run_hook_payload = optional(string, null)<br/> maximum_duration_in_seconds = optional(number, null)<br/> environment_variables = optional(map(string), {})<br/> tags = optional(map(string), {})<br/> iam = optional(object({<br/> resource_arns = optional(list(string), ["*"])<br/> actions = optional(object({<br/> scale_up = optional(list(string), null)<br/> scale_down = optional(list(string), null)<br/> }), {})<br/> additional_policy_json = optional(object({<br/> scale_up = optional(string, null)<br/> }), {})<br/> managed_policy_arns = optional(object({<br/> scale_up = optional(string, null)<br/> pool = optional(string, null)<br/> }), {})<br/> }), {})<br/> })</pre> | n/a | yes |
36+
| <a name="input_github"></a> [github](#input\_github) | GitHub Enterprise Server settings available to compute-provider bootstrap data.<br/><br/>- `enterprise_server.url`: Optional GitHub Enterprise Server base URL. Null selects GitHub.com.<br/>- `enterprise_server.ssl_verify`: Enables TLS certificate verification for GitHub Enterprise Server. | <pre>object({<br/> enterprise_server = optional(object({<br/> url = optional(string, null)<br/> ssl_verify = optional(bool, true)<br/> }), {})<br/> })</pre> | `{}` | no |
37+
| <a name="input_observability"></a> [observability](#input\_observability) | CloudWatch Logs settings available to compute-provider runner log groups.<br/><br/>- `logs.retention_in_days`: Retention period for provider-owned runner log groups.<br/>- `logs.kms_key_id`: Optional KMS key ID or ARN used to encrypt runner log groups.<br/>- `logs.tags`: Shared log-group tags that override module-level `tags`. | <pre>object({<br/> logs = optional(object({<br/> retention_in_days = optional(number, 180)<br/> kms_key_id = optional(string, null)<br/> tags = optional(map(string), {})<br/> }), {})<br/> })</pre> | `{}` | no |
38+
| <a name="input_prefix"></a> [prefix](#input\_prefix) | Prefix used to identify resources created for the runner stack. | `string` | `"github-actions"` | no |
39+
| <a name="input_runner"></a> [runner](#input\_runner) | Provider-neutral runner settings consumed by compute providers.<br/><br/>- `os`: Runner operating system. Supported values are `linux`, `osx`, and `windows`.<br/>- `architecture`: Runner distribution architecture.<br/>- `boot_time_in_minutes`: Expected boot and registration duration used by scale-down and pool.<br/>- `name_prefix`: Prefix added to registered runner names.<br/>- `run_as_root`: Runs the runner service as root.<br/>- `run_as`: Operating-system user used when `run_as_root` is false.<br/>- `hooks.job_started`: Script installed as the runner job-started hook.<br/>- `hooks.job_completed`: Script installed as the runner job-completed hook.<br/>- `iam.role.arn`: Resolved runner-role ARN referenced by provider policies and resources.<br/>- `iam.role.name`: Resolved runner-role name used by provider resources.<br/>- `iam.role.managed`: Whether runner-stack manages the resolved runner role.<br/>- `iam.managed_policy_arns`: Common managed-policy ARNs returned with the provider-specific runner policies for attachment by runner-stack.<br/>- `iam.path`: IAM path available to provider-managed IAM resources. Null derives the path from `prefix`. | <pre>object({<br/> os = optional(string, "linux")<br/> architecture = optional(string, "x64")<br/> boot_time_in_minutes = optional(number, 5)<br/> name_prefix = optional(string, "")<br/> run_as_root = optional(bool, false)<br/> run_as = optional(string, "ec2-user")<br/> hooks = optional(object({<br/> job_started = optional(string, "")<br/> job_completed = optional(string, "")<br/> }), {})<br/> iam = object({<br/> role = object({<br/> arn = string<br/> name = string<br/> managed = optional(bool, true)<br/> })<br/> managed_policy_arns = optional(map(string), {})<br/> path = optional(string, null)<br/> })<br/> })</pre> | n/a | yes |
40+
| <a name="input_ssm"></a> [ssm](#input\_ssm) | Parameter Store paths and tag scopes available to compute-provider bootstrap resources.<br/><br/>- `paths.root`: Root Parameter Store path for the runner stack.<br/>- `paths.tokens`: Path segment used for registration tokens and just-in-time configuration.<br/>- `paths.config`: Path segment used for persistent runner and provider configuration.<br/>- `tags`: Shared SSM tags that override module-level `tags`.<br/>- `parameters.tags`: Parameter-specific tags that override module-level and shared SSM tags. | <pre>object({<br/> paths = object({<br/> root = string<br/> tokens = string<br/> config = string<br/> })<br/> tags = optional(map(string), {})<br/> parameters = optional(object({<br/> tags = optional(map(string), {})<br/> }), {})<br/> })</pre> | n/a | yes |
41+
| <a name="input_tags"></a> [tags](#input\_tags) | Base tags available to taggable compute-provider resources. Provider-specific tags override this map within their documented scopes. | `map(string)` | `{}` | no |
42+
43+
## Outputs
44+
45+
| Name | Description |
46+
|------|-------------|
47+
| <a name="output_environment_variables"></a> [environment\_variables](#output\_environment\_variables) | Provider-specific Lambda environment variable fragments consumed by runner-stack. |
48+
| <a name="output_policies"></a> [policies](#output\_policies) | Provider-specific IAM policy fragments consumed by runner-stack. |
49+
| <a name="output_provider"></a> [provider](#output\_provider) | Nested Lambda MicroVM compute-provider contract consumed by runner-stack. |
50+
| <a name="output_resources"></a> [resources](#output\_resources) | Provider-specific MicroVM resources exposed by runner-stack. |
51+
<!-- END_TF_DOCS -->
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
# MicroVM runner trust policy
2+
3+
This internal submodule builds the MicroVM runner-role trust policy independently from runtime resources that consume the runner role. It preserves the default Lambda service trust and optionally merges an additional IAM trust policy document supplied by the common runner stack.
4+
5+
<!-- BEGIN_TF_DOCS -->
6+
## Requirements
7+
8+
| Name | Version |
9+
|------|---------|
10+
| <a name="requirement_terraform"></a> [terraform](#requirement\_terraform) | >= 1.4.0 |
11+
| <a name="requirement_aws"></a> [aws](#requirement\_aws) | >= 6.0 |
12+
13+
## Providers
14+
15+
| Name | Version |
16+
|------|---------|
17+
| <a name="provider_aws"></a> [aws](#provider\_aws) | >= 6.0 |
18+
19+
## Modules
20+
21+
No modules.
22+
23+
## Resources
24+
25+
| Name | Type |
26+
|------|------|
27+
| [aws_iam_policy_document.assume_role](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
28+
| [aws_iam_policy_document.default](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
29+
30+
## Inputs
31+
32+
| Name | Description | Type | Default | Required |
33+
|------|-------------|------|---------|:--------:|
34+
| <a name="input_additional_trust_policy_json"></a> [additional\_trust\_policy\_json](#input\_additional\_trust\_policy\_json) | Optional IAM policy document merged with the MicroVM provider's default runner-role trust policy. | `string` | `null` | no |
35+
36+
## Outputs
37+
38+
| Name | Description |
39+
|------|-------------|
40+
| <a name="output_assume_role_policy"></a> [assume\_role\_policy](#output\_assume\_role\_policy) | MicroVM runner-role trust policy including any additional trust statements. |
41+
<!-- END_TF_DOCS -->

0 commit comments

Comments
 (0)