diff --git a/.github/workflows/ministack.yml b/.github/workflows/ministack.yml index 34ca623f8c..3f8b7c6b38 100644 --- a/.github/workflows/ministack.yml +++ b/.github/workflows/ministack.yml @@ -54,7 +54,6 @@ jobs: - ephemeral - multi-runner - multi-runner-v2 - - multi-runner-scale-set - termination-watcher terraform: - "1.4.0" diff --git a/.github/workflows/terraform.yml b/.github/workflows/terraform.yml index c69c2f79fb..040dac6ad3 100644 --- a/.github/workflows/terraform.yml +++ b/.github/workflows/terraform.yml @@ -156,8 +156,7 @@ jobs: "termination-watcher", "multi-runner", "multi-runner-v2", - "external-managed-ssm-secrets", - "multi-runner-scale-set" + "external-managed-ssm-secrets" ] defaults: run: diff --git a/examples/multi-runner-scale-set/.terraform.lock.hcl b/examples/multi-runner-scale-set/.terraform.lock.hcl deleted file mode 100644 index c96d2b19bf..0000000000 --- a/examples/multi-runner-scale-set/.terraform.lock.hcl +++ /dev/null @@ -1,93 +0,0 @@ -# This file is maintained automatically by "terraform init". -# Manual edits may be lost in future updates. - -provider "registry.terraform.io/hashicorp/aws" { - version = "6.63.0" - constraints = ">= 5.0.0, >= 6.21.0, >= 6.33.0" - hashes = [ - "h1:9cre7jh1lSs/9igpgAcENMUAUlYW3HCtkav3up4oit0=", - "h1:dRlYHkc+r6fgzF57WC7Zjcmb6sF/6TTGDEgwGK+LAZY=", - "zh:005d56736afd17d963998c405cee6f434dbc23a415109f9435ff1542879ae611", - "zh:026ef126321a86ad7080b5d858e2527f96f5289678cbcd8856296e229c43339d", - "zh:06e0b58b2d1eddb5137fc86bee7ad2d07953c0bc3f57cccfc5ae0d2456068a3a", - "zh:07221735d61ababed84734e5ffcfc5bd59d01f29f029166ba5f2175895dceed1", - "zh:1a72db00583112bdb8c19b213a78a3f5de754fffc08f07e061f4e326289fab7d", - "zh:32968e74a53b03e97a084dc7050c22ef661fb5b3ea8a44f5a63e47bc45ad0e7c", - "zh:4b357dfe4b820e3e4acd2881cff8288b2186491e63416751f0d12692ba478ceb", - "zh:81e30884d7de686265e7d87bb92527e802878c65a378470ede2a1e9f4e40ccc9", - "zh:82e137297f6a5a08b9ce2138f7aabea245ad99495d9d9eff502f752d6ca90dbd", - "zh:8eb83b67099f0ea9df238a979dff933ff50ce06a2e3ff05a48556a10f10dd204", - "zh:9b12af85486a96aedd8d7984b0ff811a4b42e3d88dad1a3fb4c0b580d04fa425", - "zh:d0ba30886cbe41850fee689f51ef9088578f323cfd21817bb409951d43c465eb", - "zh:dd48e7089784454bc03d713e9057f5ca0ea1613bd402125054a51894957b7925", - "zh:f250fa81e54cf60fcb0e9c0fc4ac043f1ecc2ac24967f628b3609364fcab3d04", - "zh:f38fc09fc25a8d2cf89a4d4cd6a5ef7cb1aad72798dbdcad58b8876b6a551a54", - "zh:f7c7380fdf126e1901f2084588dbfd724c76cb131ccfa795a541219111103c06", - ] -} - -provider "registry.terraform.io/hashicorp/local" { - version = "2.9.0" - constraints = "~> 2.0" - hashes = [ - "h1:9rBZCMNpxKwMlRbWH2QpwD3kqUCAejdOZQ/aiiDObXQ=", - "h1:m24fjcInWvTVZ1XSo2MaNuKPe+X/gfG8SIi09rA7a7M=", - "zh:0baa4566cf77f1ff52f4293d1c8536202dd23edc197c3196413a28343c3ac3a0", - "zh:16b5559c3c07088ddad11a9bb9e9c0799999363c2958e9a5be2bcbbf2cd9ca64", - "zh:197c79015a10d1cce904a8ea722cbc750c42aeae2da53f44a6a0751d9fd1aa90", - "zh:29d0b03e5343a80677ebfeb2e2c31cbe4b1f65e736e53417454a4277fec2544c", - "zh:4896bfa6cf1d2fd562b47ef2e87f47862ae92a04f8ad5d764380f0c6653473b8", - "zh:531f8529cbca49f681883e57761a05a8398afaef6d1ab0d205d26bf12f4428e8", - "zh:6aaf5011d83161c86d2bfb80c0923ec934e578288758da2f37acb7aec129004b", - "zh:7430275253d3d3c40aa6179e0ec0d63212874dbbc06c5a51b9d07ec590f9756c", - "zh:78d5eefdd9e494defcb3c68d282b8f96630502cac21d1ea161f53cfe9bb483b3", - "zh:be17dc611e95e26cdf6cad79dfccf1064f0e32032a2efeb939a9bbe7fb1cbfe9", - "zh:f0e3b0aa644202e1d79d2000dca91f6019425da71e9800fa23f27e51c034f195", - "zh:f62bae4519e4ead49182ddc8afe8cf61e2a4c3ba3973b0fbba967736a2696aa3", - "zh:fcafa360a5b0b96244f26f4e3a6d642b716a376557142c2442ff2fb12d11da18", - ] -} - -provider "registry.terraform.io/hashicorp/null" { - version = "3.3.1" - constraints = "~> 3.0, ~> 3.2" - hashes = [ - "h1:TuxJq10DVnRP7c5HBZPyyvQGcckNVfijyU1eXEu5e4M=", - "h1:m5FqidbIgh+E9OigiZh8/xbkvpUQFSj3hZo/jqNLCLQ=", - "zh:08c59776542ea16e5a8545752787b17ff412922182b4cfabe16139197be8ac44", - "zh:123109cc7e5ed6d515787fbc212f2a3fd5e75647bb24ab7c801ccd4d4ed42451", - "zh:14b3fa4372754b54844b41d5dbd4671a292d8d6828b90169061feb4d7b15dd05", - "zh:56a4daaa3212f57b764bf3d1f333141c6610c5f21abb240e0111221f7c7fa4d4", - "zh:78d5eefdd9e494defcb3c68d282b8f96630502cac21d1ea161f53cfe9bb483b3", - "zh:7e888a026dbacd2474a42264227ae35f639780f0f0c613529d10a95cd61988b3", - "zh:85a53646267e87d600df7124e4767ffde9bba3b6356d45d961618bdd68131cc7", - "zh:8ffa0e9c7c39b2ab0905b472465d6e35ef0b776b3f6273bb34c150340b61bff1", - "zh:9846510a1841530d4403f4818e233f91e3b3bade7441047599fbf800742f65be", - "zh:afa98d44860875f037c6def0a7e6ff208e042712ba771f620482b143cd336891", - "zh:bdca130d9ef27488ae0b13bc8fd8019e8bbdd4f2ceff29da066bd333165d68c5", - "zh:cb3b94cbca88210dd0d1f11e2b8a89333f48c3857faf8f70f589072ce7c28610", - "zh:f0c0ba87925fe32f84b80f7513b1efb1b0866f51f899ba825e95ad59ff09b018", - ] -} - -provider "registry.terraform.io/hashicorp/random" { - version = "3.9.0" - constraints = "~> 3.0" - hashes = [ - "h1:OO+IuvQJSPmWdN8AyyIEvPJbLvDQpgX/zbktoa9KsJE=", - "h1:UlBuNVuCGJ39tTv2c5gz2NRZnQbXfbIWbTzWcth5o74=", - "zh:161ad0bd9a75768c82f53fb6e7172a9d8be2d4889b012645a34795031aaf1bf1", - "zh:19dc9a5b17729725ccfc4f45b0500af0ee5bc6b6b160c7adb8f2bf617d2c80ea", - "zh:269eda8fe42daa7974d5a34d166c3ba9defe80cde86c01e4dadcfdf2e1f05e5f", - "zh:373f7c65566f8f2cc7f45d698654feb9d988996957e1266a69ca00c52d6d16d0", - "zh:5599d16804c41c83009ec621b6d6b6f74e102f5827678a4750f8809055546b61", - "zh:583be0440469a22bff70dcfa56593b01566860b29607437264adb51060cf46fc", - "zh:5f211d8ec3f2e1f414870d9584bfe26e6995560ef81c748f8447a48164767398", - "zh:78d5eefdd9e494defcb3c68d282b8f96630502cac21d1ea161f53cfe9bb483b3", - "zh:7b547fd16216761ef86efc3ed516ac5ac0c5c42b7c7eb24a08cef2d93f69ed5e", - "zh:7e7c0679daf2a382151d05068c8c3f0dae6b7b7dccf818827b73dd08638df2ef", - "zh:8089dec888a8038b9b4fb23b3df7e1057293dbc5b60b42cc47ff690d69d4b61b", - "zh:c51f15a031edfd6f23ce8ced3446ca7f8d8d647e2499890d7d5d10d5016d7257", - "zh:c94784f005708890dc6895afd53636ec00ec1e430b15d41e5aebfb1d4b39bd04", - ] -} diff --git a/examples/multi-runner-scale-set/README.md b/examples/multi-runner-scale-set/README.md deleted file mode 100644 index 29c9a21b36..0000000000 --- a/examples/multi-runner-scale-set/README.md +++ /dev/null @@ -1,85 +0,0 @@ -# Multi-runner scale-set example - -This example demonstrates the experimental multi-runner v2 interface. Shared -defaults are configured with `global_config*` variables, while -each runner lane uses `multi_runner_config` for its matcher, -runner lifecycle, and compute-provider settings. - -The example creates four lanes from one deployment: - -- Linux ARM64 Amazon Linux runners. -- Ephemeral Linux x64 Amazon Linux runners with job retry enabled. -- Linux x64 runners managed by a GitHub Actions scale set. -- Windows x64 Server Core 2022 runners. - -The v2 interface keeps provider-owned settings inside the selected provider -configuration. For example, VPC and subnet settings are under -`global_config_compute_provider.aws.ec2`, while the per-lane -instance types and AMI filter are under each lane's compute provider block. - -The scale-set lane uses `orchestration_provider.scale_set`. Its controller -network is configured under the global scale-set block and its GitHub -installation ID is read from the SSM parameter described by `var.scale_set`. - -Configure the GitHub App variables before applying: - -```bash -terraform init -terraform apply \ - -var='github_app={id="123456",key_base64="..."}' \ - -var='scale_set={config_url="https://github.com/example" installation_id_ssm={name="/github/scale-set/installation-id",arn="arn:aws:ssm:eu-west-1:123456789012:parameter/github/scale-set/installation-id"} name="linux-scale-set" id=123}' -``` - -The `github_app` value is sensitive and should be supplied through a secure -variable source in real deployments rather than committed to configuration. -The scale-set installation ID must already exist in the referenced SSM -parameter and the GitHub App must be installed for the configured URL. - - -## Requirements - -| Name | Version | -|------|---------| -| [terraform](#requirement\_terraform) | >= 1.4.0 | -| [aws](#requirement\_aws) | >= 6.33 | -| [local](#requirement\_local) | ~> 2.0 | -| [random](#requirement\_random) | ~> 3.0 | - -## Providers - -| Name | Version | -|------|---------| -| [random](#provider\_random) | 3.9.0 | - -## Modules - -| Name | Source | Version | -|------|--------|---------| -| [base](#module\_base) | ../base | n/a | -| [runners](#module\_runners) | ../../modules/multi-runner | n/a | -| [webhook\_github\_app](#module\_webhook\_github\_app) | ../../modules/webhook-github-app | n/a | - -## Resources - -| Name | Type | -|------|------| -| [random_id.random](https://registry.terraform.io/providers/hashicorp/random/latest/docs/resources/id) | resource | - -## Inputs - -| Name | Description | Type | Default | Required | -|------|-------------|------|---------|:--------:| -| [ami](#input\_ami) | Optional AMI configuration keyed by runner lane. |
map(object({
filter = optional(map(list(string)), { state = ["available"] })
owners = optional(list(string), ["amazon"])
id_ssm_parameter = optional(object({
arn = string
}), null)
kms_key = optional(object({
arn = string
}), null)
})) | `{}` | no |
-| [aws\_region](#input\_aws\_region) | AWS region to deploy to. | `string` | `"eu-west-1"` | no |
-| [environment](#input\_environment) | Environment name, used as prefix. | `string` | `null` | no |
-| [github\_app](#input\_github\_app) | GitHub App ID and base64-encoded private key. | object({
id = string
key_base64 = string
}) | n/a | yes |
-| [runner\_binaries\_enabled](#input\_runner\_binaries\_enabled) | Whether runner binary synchronization is enabled. | `bool` | `true` | no |
-| [scale\_set](#input\_scale\_set) | GitHub Actions scale-set configuration. | object({
config_url = string
installation_id_ssm = object({
arn = string
name = string
})
name = string
id = number
runner_group_id = optional(number)
}) | n/a | yes |
-
-## Outputs
-
-| Name | Description |
-|------|-------------|
-| [webhook\_endpoint](#output\_webhook\_endpoint) | n/a |
-| [webhook\_secret](#output\_webhook\_secret) | n/a |
-
diff --git a/examples/multi-runner-scale-set/main.tf b/examples/multi-runner-scale-set/main.tf
deleted file mode 100644
index 816f70e7c3..0000000000
--- a/examples/multi-runner-scale-set/main.tf
+++ /dev/null
@@ -1,210 +0,0 @@
-locals {
- environment = var.environment != null ? var.environment : "multi-runner-v2"
- aws_region = var.aws_region
-}
-
-resource "random_id" "random" {
- byte_length = 20
-}
-
-module "base" {
- source = "../base"
-
- prefix = local.environment
- aws_region = local.aws_region
-}
-
-module "runners" {
- source = "../../modules/multi-runner"
-
- prefix = local.environment
- aws_region = local.aws_region
-
- experimental_features = ["multi-runner-v2"]
-
- global_config = {
- tags = {
- Example = local.environment
- Project = "ProjectX"
- }
- runner = {
- os = "linux"
- architecture = "x64"
- extra_labels = ["v2"]
- }
- }
-
- global_config_github = {
- app = {
- key_base64 = var.github_app.key_base64
- id = var.github_app.id
- webhook_secret = random_id.random.hex
- }
- }
-
- global_config_lambda = {
- architecture = "arm64"
- }
-
- global_config_orchestration_provider = {
- webhook = {
- eventbridge = {
- enabled = true
- accept_events = ["workflow_job"]
- }
- }
- scale_set = {
- grouping = {
- strategy = "runner_config"
- }
- network = {
- vpc_id = module.base.vpc.vpc_id
- subnet_ids = module.base.vpc.private_subnets
- }
- }
- }
-
- global_config_compute_provider = {
- aws = {
- ec2 = {
- vpc_id = module.base.vpc.vpc_id
- subnet_ids = module.base.vpc.private_subnets
- ssm_enabled = true
- runner_binaries = {
- enabled = var.runner_binaries_enabled
- }
- }
- }
- }
-
- multi_runner_config = {
- linux-arm64 = {
- runner = {
- architecture = "arm64"
- name_prefix = "amazon-arm64-"
- extra_labels = ["amazon"]
- }
- orchestration_provider = {
- webhook = {
- runner = {
- maximum_count = 1
- }
- matcherConfig = {
- exactMatch = true
- labelMatchers = [["self-hosted", "linux", "arm64", "amazon"]]
- }
- }
- }
- compute_provider = {
- aws = {
- ec2 = {
- instance_types = ["t4g.large", "c6g.large"]
- ami = lookup(var.ami, "linux-arm64", null)
- }
- }
- }
- }
-
- linux-x64 = {
- runner = {
- name_prefix = "amazon-x64-"
- extra_labels = ["amazon"]
- }
- orchestration_provider = {
- webhook = {
- runner = {
- ephemeral = true
- maximum_count = 1
- }
- matcherConfig = {
- labelMatchers = [["self-hosted", "linux", "x64", "amazon"]]
- exactMatch = false
- priority = 1
- }
- queue = {
- delay_webhook_event = 0
- }
- job_retry = {
- enabled = true
- }
- }
- }
- compute_provider = {
- aws = {
- ec2 = {
- instance_types = ["m5a.large", "m5ad.large"]
- ami = lookup(var.ami, "linux-x64", null)
- }
- }
- }
- }
-
- linux-scale-set = {
- runner = {
- name_prefix = "scale-set-"
- extra_labels = ["scale-set"]
- }
- orchestration_provider = {
- scale_set = {
- github = {
- config_url = var.scale_set.config_url
- installation_id_ssm = var.scale_set.installation_id_ssm
- }
- name = var.scale_set.name
- id = var.scale_set.id
- runner_group_id = var.scale_set.runner_group_id
- min_runners = 0
- max_runners = 10
- work_folder = "_work/scale-set"
- }
- }
- compute_provider = {
- aws = {
- ec2 = {
- instance_types = ["m5.large"]
- ami = lookup(var.ami, "linux-scale-set", null)
- }
- }
- }
- }
-
- windows-x64 = {
- runner = {
- os = "windows"
- name_prefix = "windows-x64-"
- }
- orchestration_provider = {
- webhook = {
- runner = {
- boot_time_in_minutes = 20
- maximum_count = 1
- }
- matcherConfig = {
- exactMatch = true
- labelMatchers = [["self-hosted", "windows", "x64", "servercore-2022"]]
- }
- }
- }
- compute_provider = {
- aws = {
- ec2 = {
- instance_types = ["m5.large", "c5.large"]
- ami = lookup(var.ami, "windows-x64", null)
- }
- }
- }
- }
- }
-}
-
-module "webhook_github_app" {
- source = "../../modules/webhook-github-app"
- depends_on = [module.runners]
-
- github_app = {
- key_base64 = var.github_app.key_base64
- id = var.github_app.id
- webhook_secret = random_id.random.hex
- }
- webhook_endpoint = module.runners.webhook.endpoint
-}
diff --git a/examples/multi-runner-scale-set/outputs.tf b/examples/multi-runner-scale-set/outputs.tf
deleted file mode 100644
index 1feaf2e671..0000000000
--- a/examples/multi-runner-scale-set/outputs.tf
+++ /dev/null
@@ -1,8 +0,0 @@
-output "webhook_endpoint" {
- value = module.runners.webhook.endpoint
-}
-
-output "webhook_secret" {
- sensitive = true
- value = random_id.random.hex
-}
diff --git a/examples/multi-runner-scale-set/providers.tf b/examples/multi-runner-scale-set/providers.tf
deleted file mode 100644
index eca2fe96a7..0000000000
--- a/examples/multi-runner-scale-set/providers.tf
+++ /dev/null
@@ -1,9 +0,0 @@
-provider "aws" {
- region = local.aws_region
-
- default_tags {
- tags = {
- Example = local.environment
- }
- }
-}
diff --git a/examples/multi-runner-scale-set/variables.tf b/examples/multi-runner-scale-set/variables.tf
deleted file mode 100644
index 1c4fcc4e4d..0000000000
--- a/examples/multi-runner-scale-set/variables.tf
+++ /dev/null
@@ -1,61 +0,0 @@
-variable "github_app" {
- description = "GitHub App ID and base64-encoded private key."
-
- type = object({
- id = string
- key_base64 = string
- })
- sensitive = true
-}
-
-variable "scale_set" {
- description = "GitHub Actions scale-set configuration."
-
- type = object({
- config_url = string
- installation_id_ssm = object({
- arn = string
- name = string
- })
- name = string
- id = number
- runner_group_id = optional(number)
- })
-}
-
-variable "environment" {
- description = "Environment name, used as prefix."
-
- type = string
- default = null
-}
-
-variable "aws_region" {
- description = "AWS region to deploy to."
-
- type = string
- default = "eu-west-1"
-}
-
-variable "runner_binaries_enabled" {
- description = "Whether runner binary synchronization is enabled."
-
- type = bool
- default = true
-}
-
-variable "ami" {
- description = "Optional AMI configuration keyed by runner lane."
-
- type = map(object({
- filter = optional(map(list(string)), { state = ["available"] })
- owners = optional(list(string), ["amazon"])
- id_ssm_parameter = optional(object({
- arn = string
- }), null)
- kms_key = optional(object({
- arn = string
- }), null)
- }))
- default = {}
-}
diff --git a/examples/multi-runner-scale-set/versions.tf b/examples/multi-runner-scale-set/versions.tf
deleted file mode 100644
index 1dfb3e5774..0000000000
--- a/examples/multi-runner-scale-set/versions.tf
+++ /dev/null
@@ -1,17 +0,0 @@
-terraform {
- required_providers {
- aws = {
- source = "hashicorp/aws"
- version = ">= 6.33"
- }
- local = {
- source = "hashicorp/local"
- version = "~> 2.0"
- }
- random = {
- source = "hashicorp/random"
- version = "~> 3.0"
- }
- }
- required_version = ">= 1.4.0"
-}
diff --git a/examples/multi-runner-v2/README.md b/examples/multi-runner-v2/README.md
index f18735e35d..eafe371463 100644
--- a/examples/multi-runner-v2/README.md
+++ b/examples/multi-runner-v2/README.md
@@ -67,7 +67,6 @@ variable source in real deployments rather than committed to configuration.
| [aws\_region](#input\_aws\_region) | AWS region to deploy to. | `string` | `"eu-west-1"` | no |
| [environment](#input\_environment) | Environment name, used as prefix. | `string` | `null` | no |
| [github\_app](#input\_github\_app) | GitHub App ID and base64-encoded private key. | object({
id = string
key_base64 = string
}) | n/a | yes |
-| [runner\_binaries\_enabled](#input\_runner\_binaries\_enabled) | Whether runner binary synchronization is enabled. | `bool` | `true` | no |
## Outputs
diff --git a/examples/multi-runner-v2/main.tf b/examples/multi-runner-v2/main.tf
index 2076c2a4a2..fd21d351ac 100644
--- a/examples/multi-runner-v2/main.tf
+++ b/examples/multi-runner-v2/main.tf
@@ -60,7 +60,7 @@ module "runners" {
subnet_ids = module.base.vpc.private_subnets
ssm_enabled = true
runner_binaries = {
- enabled = var.runner_binaries_enabled
+ enabled = true
}
}
}
diff --git a/examples/multi-runner-v2/variables.tf b/examples/multi-runner-v2/variables.tf
index b6f4d7588b..fe104758b9 100644
--- a/examples/multi-runner-v2/variables.tf
+++ b/examples/multi-runner-v2/variables.tf
@@ -22,13 +22,6 @@ variable "aws_region" {
default = "eu-west-1"
}
-variable "runner_binaries_enabled" {
- description = "Whether runner binary synchronization is enabled."
-
- type = bool
- default = true
-}
-
variable "ami" {
description = "Optional AMI configuration keyed by runner lane."
diff --git a/modules/multi-runner/README.md b/modules/multi-runner/README.md
index c07cdaa797..59599e1eb4 100644
--- a/modules/multi-runner/README.md
+++ b/modules/multi-runner/README.md
@@ -4,8 +4,6 @@
This module creates many runners with one or more GitHub Apps. The module utilizes the internal modules and deploys parts of the stack for each runner defined.
-Terraform 1.4 or later is required. Terraform 1.3 and earlier are no longer supported by this module.
-
### GitHub App round-robin
To distribute GitHub API rate limit usage, this module supports configuring multiple GitHub Apps via the `additional_github_apps` variable. The control-plane lambdas (scale-up, scale-down, pool, job-retry) randomly select an app for each API call, spreading the load across all configured apps.
diff --git a/modules/multi-runner/config.experimental.resolved.tf b/modules/multi-runner/config.experimental.resolved.tf
index bff2936659..5753b19c8b 100644
--- a/modules/multi-runner/config.experimental.resolved.tf
+++ b/modules/multi-runner/config.experimental.resolved.tf
@@ -295,7 +295,7 @@ locals {
tags = merge(local.normalized_config.orchestration_provider.webhook.queue.tags, v.orchestration_provider.webhook.queue.tags)
})
})
- scale_set = try(v.orchestration_provider.scale_set, null)
+ scale_set = v.orchestration_provider.scale_set
}
ssm = merge(v.ssm, {
diff --git a/modules/multi-runner/outputs.tf b/modules/multi-runner/outputs.tf
index ce9019c1d7..0492d0faba 100644
--- a/modules/multi-runner/outputs.tf
+++ b/modules/multi-runner/outputs.tf
@@ -54,38 +54,28 @@ output "binaries_syncer_map" {
}
output "webhook" {
- value = {
- gateway = module.webhook.gateway
- lambda = module.webhook.lambda
- lambda_log_group = module.webhook.lambda_log_group
- lambda_role = module.webhook.role
- endpoint = "${module.webhook.gateway.api_endpoint}/${module.webhook.endpoint_relative_path}"
- webhook = module.webhook.webhook
- dispatcher = local.effective_config.orchestration_provider.webhook.eventbridge.enabled ? module.webhook.dispatcher : null
- eventbridge = local.effective_config.orchestration_provider.webhook.eventbridge.enabled ? module.webhook.eventbridge : null
+ value = length(module.webhook) == 0 ? null : {
+ gateway = module.webhook[0].gateway
+ lambda = module.webhook[0].lambda
+ lambda_log_group = module.webhook[0].lambda_log_group
+ lambda_role = module.webhook[0].role
+ endpoint = "${module.webhook[0].gateway.api_endpoint}/${module.webhook[0].endpoint_relative_path}"
+ webhook = module.webhook[0].webhook
+ dispatcher = length(module.webhook) > 0 && try(local.effective_config.orchestration_provider.webhook.eventbridge.enabled, false) ? module.webhook[0].dispatcher : null
+ eventbridge = length(module.webhook) > 0 && try(local.effective_config.orchestration_provider.webhook.eventbridge.enabled, false) ? module.webhook[0].eventbridge : null
}
}
output "ssm_parameters" {
- value = merge(
- {
- id = { name = local.github_app_parameters.id[0].name, arn = local.github_app_parameters.id[0].arn }
- key_base64 = { name = local.github_app_parameters.key_base64[0].name, arn = local.github_app_parameters.key_base64[0].arn }
- webhook_secret = { name = local.github_app_parameters.webhook_secret.name, arn = local.github_app_parameters.webhook_secret.arn }
- },
- { for idx, v in local.github_app_parameters.id : "github_app_id_${idx}" => {
- name = v.name
- arn = v.arn
- } },
- { for idx, v in local.github_app_parameters.key_base64 : "github_app_key_base64_${idx}" => {
- name = v.name
- arn = v.arn
- } },
- { "github_app_webhook_secret" = {
- name = local.github_app_parameters.webhook_secret.name
- arn = local.github_app_parameters.webhook_secret.arn
- } },
- )
+ value = {
+ id = { name = local.github_app_parameters.id.name, arn = local.github_app_parameters.id.arn }
+ key_base64 = { name = local.github_app_parameters.key_base64.name, arn = local.github_app_parameters.key_base64.arn }
+ webhook_secret = { name = local.github_app_parameters.webhook_secret.name, arn = local.github_app_parameters.webhook_secret.arn }
+ additional_apps_manifest = local.github_app_parameters.additional_apps_manifest != null ? {
+ name = local.github_app_parameters.additional_apps_manifest.name
+ arn = local.github_app_parameters.additional_apps_manifest.arn
+ } : null
+ }
}
output "instance_termination_watcher" {
diff --git a/modules/multi-runner/tests/config-translation.tftest.hcl b/modules/multi-runner/tests/config-translation.tftest.hcl
index 030b911b2d..162002c95e 100644
--- a/modules/multi-runner/tests/config-translation.tftest.hcl
+++ b/modules/multi-runner/tests/config-translation.tftest.hcl
@@ -405,7 +405,7 @@ run "empty_v2_map_translates_stable_inputs" {
&& local.stable_to_v2.github.user_agent == var.user_agent
&& local.stable_to_v2.lambda.artifact.s3.bucket == var.lambda_s3_bucket
&& local.stable_to_v2.orchestration_provider.webhook.lambda.scale.up.event_source_mapping.batch_size == var.lambda_event_source_mapping_batch_size
- && local.stable_to_v2.orchestration_provider.webhook.lambda.scale.down.idle_config == []
+ && length(local.stable_to_v2.orchestration_provider.webhook.lambda.scale.down.idle_config) == 0
&& local.stable_to_v2.ssm.parameters.tags.owner == var.parameter_store_tags.owner
&& local.stable_to_v2.ssm.housekeeper.lambda.memory_size == var.runners_ssm_housekeeper.lambda_memory_size
&& local.stable_to_v2.compute_provider.aws.ec2.runner_binaries.s3.encryption.sse_algorithm == "aws:kms"
@@ -548,7 +548,7 @@ run "v2_entry_without_matcher_config_is_authoritative" {
}
}
name = "no-matcher-scale-set"
- id = 42
+ id = 1
}
}
compute_provider = {
@@ -569,7 +569,6 @@ run "v2_entry_without_matcher_config_is_authoritative" {
local.use_v2_config
&& toset(keys(local.normalized_config.multi_runner_config)) == toset(["no_matcher"])
&& try(local.normalized_config.multi_runner_config["no_matcher"].orchestration_provider.webhook.matcherConfig, null) == null
- && local.normalized_config.multi_runner_config["no_matcher"].orchestration_provider.scale_set.name == "no-matcher-scale-set"
)
error_message = "A v2 runner entry must be recognized without requiring matcher configuration."
}
diff --git a/modules/multi-runner/tests/scale-set.tftest.hcl b/modules/multi-runner/tests/scale-set.tftest.hcl
index 7aeebf5c43..db63afd21d 100644
--- a/modules/multi-runner/tests/scale-set.tftest.hcl
+++ b/modules/multi-runner/tests/scale-set.tftest.hcl
@@ -63,8 +63,6 @@ variables {
aws_partition = "aws"
prefix = "scale-set-test"
- experimental_features = ["multi-runner-v2"]
-
global_config = {
runner = {
os = "linux"
diff --git a/modules/multi-runner/webhook.tf b/modules/multi-runner/webhook.tf
index f8d16406fe..55bfc70d39 100644
--- a/modules/multi-runner/webhook.tf
+++ b/modules/multi-runner/webhook.tf
@@ -23,15 +23,16 @@ locals {
module "webhook" {
source = "../webhook"
+ count = length(local.webhook_runner_config) > 0 ? 1 : 0
prefix = var.prefix
tags = local.tags
kms_key_arn = local.effective_config.ssm.kms_key_id
eventbridge = {
- enable = local.effective_config.orchestration_provider.webhook.eventbridge.enabled
- accept_events = local.effective_config.orchestration_provider.webhook.eventbridge.accept_events
+ enable = try(local.effective_config.orchestration_provider.webhook.eventbridge.enabled, false)
+ accept_events = try(local.effective_config.orchestration_provider.webhook.eventbridge.accept_events, [])
}
runner_matcher_config = local.runner_matcher_config
- matcher_config_parameter_store_tier = local.effective_config.orchestration_provider.webhook.matcher_config_parameter_store_tier
+ matcher_config_parameter_store_tier = try(local.effective_config.orchestration_provider.webhook.matcher_config_parameter_store_tier, "Standard")
ssm_paths = {
root = local.ssm_root_path
@@ -45,13 +46,13 @@ module "webhook" {
lambda_s3_bucket = try(local.effective_config.lambda.artifact.s3.bucket, null)
webhook_lambda_s3_key = try(local.effective_config.orchestration_provider.webhook.lambda.webhook.artifact.s3.key, null)
webhook_lambda_s3_object_version = try(local.effective_config.orchestration_provider.webhook.lambda.webhook.artifact.s3.object_version, null)
- webhook_lambda_apigateway_access_log_settings = local.effective_config.orchestration_provider.webhook.lambda.webhook.api_gateway_access_log_settings
+ webhook_lambda_apigateway_access_log_settings = try(local.effective_config.orchestration_provider.webhook.lambda.webhook.api_gateway_access_log_settings, null)
lambda_runtime = local.effective_config.lambda.runtime
lambda_architecture = local.effective_config.lambda.architecture
- lambda_zip = local.effective_config.orchestration_provider.webhook.lambda.webhook.artifact.zip
- lambda_timeout = local.effective_config.orchestration_provider.webhook.lambda.webhook.timeout
- lambda_memory_size = local.effective_config.orchestration_provider.webhook.lambda.webhook.memory_size
- lambda_tags = local.effective_config.orchestration_provider.webhook.lambda.webhook.tags
+ lambda_zip = try(local.effective_config.orchestration_provider.webhook.lambda.webhook.artifact.zip, null)
+ lambda_timeout = try(local.effective_config.orchestration_provider.webhook.lambda.webhook.timeout, null)
+ lambda_memory_size = try(local.effective_config.orchestration_provider.webhook.lambda.webhook.memory_size, null)
+ lambda_tags = try(local.effective_config.orchestration_provider.webhook.lambda.webhook.tags, {})
tracing_config = local.effective_config.observability.tracing
logging_retention_in_days = local.effective_config.observability.logs.retention_in_days
logging_kms_key_id = local.effective_config.observability.logs.kms_key_id
@@ -59,8 +60,8 @@ module "webhook" {
role_path = local.effective_config.roles.path
role_permissions_boundary = local.effective_config.roles.permissions_boundary
- repository_white_list = local.effective_config.orchestration_provider.webhook.github.repository_white_list
- queue_selection_strategy = local.effective_config.orchestration_provider.webhook.queue_selection_strategy
+ repository_white_list = try(local.effective_config.orchestration_provider.webhook.github.repository_white_list, [])
+ queue_selection_strategy = try(local.effective_config.orchestration_provider.webhook.queue_selection_strategy, "first")
lambda_subnet_ids = local.effective_config.lambda.subnet_ids
lambda_security_group_ids = local.effective_config.lambda.security_group_ids
diff --git a/modules/orchestration-providers/scale-set/validations.tf b/modules/orchestration-providers/scale-set/validations.tf
index 81a34368d3..0d29a31e5d 100644
--- a/modules/orchestration-providers/scale-set/validations.tf
+++ b/modules/orchestration-providers/scale-set/validations.tf
@@ -72,7 +72,7 @@ resource "terraform_data" "validate_contract" {
length(parameter.name) <= 2048 &&
can(regex("^/[A-Za-z0-9_./-]+$", parameter.name)) &&
!endswith(parameter.name, "/") &&
- !can(regex("//", parameter.name)) &&
+ !strcontains(parameter.name, "//") &&
parameter.arn == format(
"arn:%s:ssm:%s:%s:parameter%s",
data.aws_partition.current.partition,
@@ -113,7 +113,7 @@ resource "terraform_data" "validate_contract" {
(runner_config.work_folder == null ? true : (
length(runner_config.work_folder) <= 128 &&
!startswith(runner_config.work_folder, "/") &&
- !can(regex("\\\\", runner_config.work_folder)) &&
+ !strcontains(runner_config.work_folder, "\\") &&
can(regex("^[A-Za-z0-9._/-]+$", runner_config.work_folder)) &&
alltrue([for part in split("/", runner_config.work_folder) : !contains(["", ".", ".."], part)])
)) &&
@@ -152,7 +152,7 @@ resource "terraform_data" "validate_contract" {
can(regex("^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$", statement_name)) &&
length(statement.actions) > 0 &&
length(statement.resources) > 0 &&
- alltrue([for action in statement.actions : !can(regex("\\*", action))]) &&
+ alltrue([for action in statement.actions : !strcontains(action, "*")]) &&
alltrue([
for condition in statement.conditions : (
length(condition.test) > 0 &&
@@ -334,7 +334,7 @@ resource "terraform_data" "validate_runtime" {
length(var.network.subnet_ids) > 0 &&
length(var.network.https_egress.ipv4_cidrs) + length(var.network.https_egress.ipv6_cidrs) > 0 &&
alltrue([for cidr in var.network.https_egress.ipv4_cidrs : can(cidrnetmask(cidr))]) &&
- alltrue([for cidr in var.network.https_egress.ipv6_cidrs : can(cidrhost(cidr, 0)) && can(regex(":", cidr))])
+ alltrue([for cidr in var.network.https_egress.ipv6_cidrs : can(cidrhost(cidr, 0)) && strcontains(cidr, ":")])
)
error_message = "network must select a VPC and at least one subnet, and HTTPS egress must contain valid IPv4 or IPv6 CIDRs."
}
diff --git a/modules/orchestration-providers/webhook/README.md b/modules/orchestration-providers/webhook/README.md
index f17e3ecd26..53d0115ebb 100644
--- a/modules/orchestration-providers/webhook/README.md
+++ b/modules/orchestration-providers/webhook/README.md
@@ -40,7 +40,7 @@ The scale-down lifecycle is documented in the [scale-down state diagram](./scale
|------|-------------|------|---------|:--------:|
| [aws\_partition](#input\_aws\_partition) | AWS partition used to construct ARNs. | `string` | `"aws"` | no |
| [config](#input\_config) | Provider-owned webhook values supplied from `orchestration_provider.webhook`. The parent resolves inherited input values before calling this module; this provider still resolves the documented JIT, artifact, and tag-precedence fallbacks.object({
runner = object({
boot_time_in_minutes = number
ephemeral = bool
jit_config_enabled = optional(bool, null)
maximum_count = number
})
github = object({
organization_runners = bool
})
queue = object({
build = object({
arn = string
url = string
})
kms_key_id = optional(string, null)
tags = optional(map(string), {})
})
lambda = object({
artifact = object({
zip = optional(string, null)
s3 = optional(object({
key = string
object_version = optional(string, null)
}), null)
})
scale = object({
up = object({
memory_size = number
timeout = number
reserved_concurrent_executions = number
job_queued_check_enabled = optional(bool, null)
event_source_mapping = object({
batch_size = number
maximum_batching_window_in_seconds = number
})
tags = optional(map(string), {})
})
down = object({
memory_size = number
timeout = number
schedule_expression = string
minimum_running_time_in_minutes = optional(number, null)
idle_config = list(object({
cron = string
timeZone = string
idleCount = number
evictionStrategy = string
}))
tags = optional(map(string), {})
})
})
pool = object({
memory_size = number
timeout = number
reserved_concurrent_executions = number
config = list(object({
schedule_expression = string
schedule_expression_timezone = optional(string)
size = number
}))
include_busy_runners = bool
runner_owner = optional(string, null)
tags = optional(map(string), {})
})
})
job_retry = object({
enabled = bool
delay_in_seconds = number
delay_backoff = number
max_attempts = number
tags = optional(map(string), {})
lambda = object({
memory_size = number
reserved_concurrent_executions = number
timeout = number
})
})
}) | n/a | yes |
-| [github](#input\_github) | Common GitHub API client and GitHub App Parameter Store references. | object({
app_parameters = object({
key_base64 = list(map(string))
id = list(map(string))
installation_id = optional(list(object({ name = string, arn = string })), [null])
})
enterprise_server = object({
url = optional(string, null)
ssl_verify = bool
})
user_agent = optional(string, null)
}) | n/a | yes |
+| [github](#input\_github) | Common GitHub API client and GitHub App Parameter Store references. | object({
app_parameters = object({
key_base64 = list(map(string))
id = list(map(string))
installation_id = list(object({ name = string, arn = string }))
})
enterprise_server = object({
url = optional(string, null)
ssl_verify = bool
})
user_agent = optional(string, null)
}) | n/a | yes |
| [lambda](#input\_lambda) | Common Lambda substrate. Only the shared artifact bucket crosses this boundary; the webhook provider owns its archive key, version, and local zip selection. | object({
artifact = object({
s3 = object({
bucket = optional(string, null)
})
})
runtime = string
architecture = string
subnet_ids = list(string)
security_group_ids = list(string)
tags = optional(map(string), {})
role = object({
path = string
permissions_boundary = optional(string, null)
principals = optional(list(object({
type = string
identifiers = list(string)
})), [])
})
}) | n/a | yes |
| [observability](#input\_observability) | Common logging, tracing, and metrics configuration consumed by webhook controls. | object({
logs = object({
level = string
retention_in_days = number
kms_key_id = optional(string, null)
class = string
tags = optional(map(string), {})
})
tracing = object({
mode = optional(string, null)
capture_http_requests = bool
capture_error = bool
})
metrics = object({
enabled = bool
namespace = string
metric = object({
github_app_rate_limit = object({
enabled = bool
})
job_retry = object({
enabled = bool
})
})
})
}) | n/a | yes |
| [prefix](#input\_prefix) | Prefix used to identify resources created for this webhook orchestration provider. | `string` | n/a | yes |
diff --git a/modules/orchestration-providers/webhook/job-retry/README.md b/modules/orchestration-providers/webhook/job-retry/README.md
index c67f19ec3e..9c6e4e0f52 100644
--- a/modules/orchestration-providers/webhook/job-retry/README.md
+++ b/modules/orchestration-providers/webhook/job-retry/README.md
@@ -52,7 +52,7 @@ No modules.
| Name | Description | Type | Default | Required |
|------|-------------|------|---------|:--------:|
-| [config](#input\_config) | Provider-neutral job-retry configuration assembled by runner-config.object({
prefix = string
aws_partition = string
lambda = object({
artifact = object({
zip = string
s3 = object({
bucket = optional(string, null)
key = optional(string, null)
object_version = optional(string, null)
})
})
runtime = string
architecture = string
memory_size = number
timeout = number
reserved_concurrent_executions = number
environment_variables = map(string)
vpc = object({
subnet_ids = list(string)
security_group_ids = list(string)
})
role = object({
path = string
permissions_boundary = optional(string, null)
principals = list(object({
type = string
identifiers = list(string)
}))
})
})
runner = object({
name_prefix = string
})
github = object({
organization_runners = bool
enterprise_server = object({
url = optional(string, null)
ssl_verify = optional(bool, true)
})
user_agent = optional(string, null)
app_parameters = object({
key_base64 = list(map(string))
id = list(map(string))
installation_id = optional(list(object({ name = string, arn = string })), [null])
})
})
queue = object({
build = object({
url = string
arn = string
})
kms_key_id = optional(string, null)
event_source_mapping = object({
batch_size = number
maximum_batching_window_in_seconds = number
})
encryption = object({
sqs_managed_sse_enabled = bool
kms_master_key_id = optional(string, null)
kms_data_key_reuse_period_seconds = optional(number, null)
})
})
ssm = object({
kms_key_id = optional(string, null)
})
observability = object({
logs = object({
level = string
retention_in_days = number
kms_key_id = optional(string, null)
class = string
})
tracing = object({
mode = optional(string, null)
capture_http_requests = bool
capture_error = bool
})
metrics = object({
enabled = bool
namespace = string
metric = object({
github_app_rate_limit = object({
enabled = bool
})
job_retry = object({
enabled = bool
})
})
})
})
tags = object({
resources = map(string)
lambda = map(string)
log_group = map(string)
queue = map(string)
event_source_mapping = map(string)
})
}) | n/a | yes |
+| [config](#input\_config) | Provider-neutral job-retry configuration assembled by runner-config.object({
prefix = string
aws_partition = string
lambda = object({
artifact = object({
zip = string
s3 = object({
bucket = optional(string, null)
key = optional(string, null)
object_version = optional(string, null)
})
})
runtime = string
architecture = string
memory_size = number
timeout = number
reserved_concurrent_executions = number
environment_variables = map(string)
vpc = object({
subnet_ids = list(string)
security_group_ids = list(string)
})
role = object({
path = string
permissions_boundary = optional(string, null)
principals = list(object({
type = string
identifiers = list(string)
}))
})
})
runner = object({
name_prefix = string
})
github = object({
organization_runners = bool
enterprise_server = object({
url = optional(string, null)
ssl_verify = optional(bool, true)
})
user_agent = optional(string, null)
app_parameters = object({
key_base64 = list(map(string))
id = list(map(string))
installation_id = list(object({ name = string, arn = string }))
})
})
queue = object({
build = object({
url = string
arn = string
})
kms_key_id = optional(string, null)
event_source_mapping = object({
batch_size = number
maximum_batching_window_in_seconds = number
})
encryption = object({
sqs_managed_sse_enabled = bool
kms_master_key_id = optional(string, null)
kms_data_key_reuse_period_seconds = optional(number, null)
})
})
ssm = object({
kms_key_id = optional(string, null)
})
observability = object({
logs = object({
level = string
retention_in_days = number
kms_key_id = optional(string, null)
class = string
})
tracing = object({
mode = optional(string, null)
capture_http_requests = bool
capture_error = bool
})
metrics = object({
enabled = bool
namespace = string
metric = object({
github_app_rate_limit = object({
enabled = bool
})
job_retry = object({
enabled = bool
})
})
})
})
tags = object({
resources = map(string)
lambda = map(string)
log_group = map(string)
queue = map(string)
event_source_mapping = map(string)
})
}) | n/a | yes |
## Outputs
diff --git a/modules/orchestration-providers/webhook/job-retry/variables.tf b/modules/orchestration-providers/webhook/job-retry/variables.tf
index fe7e511289..e8235265f8 100644
--- a/modules/orchestration-providers/webhook/job-retry/variables.tf
+++ b/modules/orchestration-providers/webhook/job-retry/variables.tf
@@ -87,7 +87,7 @@ variable "config" {
app_parameters = object({
key_base64 = list(map(string))
id = list(map(string))
- installation_id = optional(list(object({ name = string, arn = string })), [null])
+ installation_id = list(object({ name = string, arn = string }))
})
})
queue = object({
diff --git a/modules/orchestration-providers/webhook/pool/README.md b/modules/orchestration-providers/webhook/pool/README.md
index f18cdd76e7..877eec8039 100644
--- a/modules/orchestration-providers/webhook/pool/README.md
+++ b/modules/orchestration-providers/webhook/pool/README.md
@@ -54,7 +54,7 @@ No modules.
| Name | Description | Type | Default | Required |
|------|-------------|------|---------|:--------:|
| [aws\_partition](#input\_aws\_partition) | (optional) partition for the arn if not 'aws' | `string` | `"aws"` | no |
-| [config](#input\_config) | Configuration passed from the webhook orchestration provider to the pool Lambda and scheduler.object({
lambda = object({
log_level = string
logging_retention_in_days = number
logging_kms_key_id = string
log_class = string
reserved_concurrent_executions = number
s3_bucket = string
s3_key = string
s3_object_version = string
security_group_ids = list(string)
runtime = string
architecture = string
memory_size = number
timeout = number
zip = string
subnet_ids = list(string)
parameter_store_tags = string
principals = optional(list(object({
type = string
identifiers = list(string)
})), [])
})
tags = map(string)
ghes = object({
url = string
ssl_verify = string
})
github_app_parameters = object({
key_base64 = list(map(string))
id = list(map(string))
installation_id = optional(list(object({ name = string, arn = string })), [null])
})
runner = object({
disable_runner_autoupdate = bool
ephemeral = bool
enable_jit_config = bool
labels = list(string)
group_name = string
name_prefix = string
pool_owner = string
boot_time_in_minutes = number
})
runners_maximum_count = number
prefix = string
pool = list(object({
schedule_expression = string
schedule_expression_timezone = string
size = number
}))
include_busy_runners = bool
role_permissions_boundary = string
kms_key_id = optional(string, null)
role_path = string
ssm_token_path = string
ssm_token_path_arn = string
ssm_config_path = string
arn_ssm_parameters_path_config = string
lambda_tags = map(string)
log_group_tags = optional(map(string), {})
user_agent = string
}) | n/a | yes |
+| [config](#input\_config) | Configuration passed from the webhook orchestration provider to the pool Lambda and scheduler.object({
lambda = object({
log_level = string
logging_retention_in_days = number
logging_kms_key_id = string
log_class = string
reserved_concurrent_executions = number
s3_bucket = string
s3_key = string
s3_object_version = string
security_group_ids = list(string)
runtime = string
architecture = string
memory_size = number
timeout = number
zip = string
subnet_ids = list(string)
parameter_store_tags = string
principals = optional(list(object({
type = string
identifiers = list(string)
})), [])
})
tags = map(string)
ghes = object({
url = string
ssl_verify = string
})
github_app_parameters = object({
key_base64 = list(map(string))
id = list(map(string))
installation_id = list(object({ name = string, arn = string }))
})
runner = object({
disable_runner_autoupdate = bool
ephemeral = bool
enable_jit_config = bool
labels = list(string)
group_name = string
name_prefix = string
pool_owner = string
boot_time_in_minutes = number
})
runners_maximum_count = number
prefix = string
pool = list(object({
schedule_expression = string
schedule_expression_timezone = string
size = number
}))
include_busy_runners = bool
role_permissions_boundary = string
kms_key_id = optional(string, null)
role_path = string
ssm_token_path = string
ssm_token_path_arn = string
ssm_config_path = string
arn_ssm_parameters_path_config = string
lambda_tags = map(string)
log_group_tags = optional(map(string), {})
user_agent = string
}) | n/a | yes |
| [runner\_provider](#input\_runner\_provider) | Compute provider integration used by the pool Lambda.object({
type = string
environment_variables = map(string)
iam_policy_json = string
managed_policy_enabled = bool
managed_policy_arn = optional(string, null)
}) | n/a | yes |
| [tracing\_config](#input\_tracing\_config) | Tracing configuration for the pool Lambda.object({
mode = optional(string, null)
capture_http_requests = optional(bool, false)
capture_error = optional(bool, false)
}) | `{}` | no |
diff --git a/modules/orchestration-providers/webhook/pool/variables.tf b/modules/orchestration-providers/webhook/pool/variables.tf
index d455f080a1..e1f516c8ad 100644
--- a/modules/orchestration-providers/webhook/pool/variables.tf
+++ b/modules/orchestration-providers/webhook/pool/variables.tf
@@ -86,7 +86,7 @@ variable "config" {
github_app_parameters = object({
key_base64 = list(map(string))
id = list(map(string))
- installation_id = optional(list(object({ name = string, arn = string })), [null])
+ installation_id = list(object({ name = string, arn = string }))
})
runner = object({
disable_runner_autoupdate = bool
diff --git a/modules/orchestration-providers/webhook/scale-runners/README.md b/modules/orchestration-providers/webhook/scale-runners/README.md
index 7bd88312f3..3b096f9b85 100644
--- a/modules/orchestration-providers/webhook/scale-runners/README.md
+++ b/modules/orchestration-providers/webhook/scale-runners/README.md
@@ -67,7 +67,7 @@ No modules.
| Name | Description | Type | Default | Required |
|------|-------------|------|---------|:--------:|
| [aws\_partition](#input\_aws\_partition) | AWS partition used to construct IAM policy ARNs. | `string` | `"aws"` | no |
-| [config](#input\_config) | Provider-neutral scale-up and scale-down configuration assembled by runner-config.object({
prefix = string
lambda = object({
artifact = object({
zip = string
s3 = object({
bucket = optional(string, null)
key = optional(string, null)
object_version = optional(string, null)
})
})
runtime = string
architecture = string
vpc = object({
subnet_ids = list(string)
security_group_ids = list(string)
})
role = object({
path = string
permissions_boundary = optional(string, null)
principals = optional(list(object({
type = string
identifiers = list(string)
})), [])
})
})
runner = object({
os = string
auto_update_disabled = bool
ephemeral = bool
jit_config_enabled = optional(bool, null)
labels = list(string)
group_name = string
name_prefix = string
boot_time_in_minutes = number
maximum_count = number
})
github = object({
organization_runners = bool
enterprise_server = object({
url = optional(string, null)
ssl_verify = bool
})
user_agent = optional(string, null)
app_parameters = object({
key_base64 = list(map(string))
id = list(map(string))
installation_id = optional(list(object({ name = string, arn = string })), [null])
})
})
queue = object({
build = object({
arn = string
})
kms_key_id = optional(string, null)
event_source_mapping = object({
batch_size = number
maximum_batching_window_in_seconds = number
})
})
ssm = object({
token_path = string
token_path_arn = string
config_path = string
config_path_arn = string
parameter_store_tags = string
kms_key_id = optional(string, null)
})
observability = object({
logs = object({
level = string
retention_in_days = number
kms_key_id = optional(string, null)
class = string
})
tracing = object({
mode = optional(string, null)
capture_http_requests = bool
capture_error = bool
})
metrics = object({
enabled = bool
namespace = string
metric = object({
github_app_rate_limit = object({
enabled = bool
})
})
})
})
scale_up = object({
memory_size = number
timeout = number
reserved_concurrent_executions = number
job_queued_check_enabled = bool
tags = object({
resources = map(string)
lambda = map(string)
log_group = map(string)
event_source_mapping = map(string)
})
})
scale_down = object({
memory_size = number
timeout = number
schedule_expression = string
minimum_running_time_in_minutes = optional(number, null)
idle_config = list(object({
cron = string
timeZone = string
idleCount = number
evictionStrategy = string
}))
tags = object({
resources = map(string)
lambda = map(string)
log_group = map(string)
})
})
job_retry = object({
enabled = bool
max_attempts = number
delay_in_seconds = number
delay_backoff = number
queue = optional(object({
arn = string
url = string
}), null)
})
}) | n/a | yes |
+| [config](#input\_config) | Provider-neutral scale-up and scale-down configuration assembled by runner-config.object({
prefix = string
lambda = object({
artifact = object({
zip = string
s3 = object({
bucket = optional(string, null)
key = optional(string, null)
object_version = optional(string, null)
})
})
runtime = string
architecture = string
vpc = object({
subnet_ids = list(string)
security_group_ids = list(string)
})
role = object({
path = string
permissions_boundary = optional(string, null)
principals = optional(list(object({
type = string
identifiers = list(string)
})), [])
})
})
runner = object({
os = string
auto_update_disabled = bool
ephemeral = bool
jit_config_enabled = optional(bool, null)
labels = list(string)
group_name = string
name_prefix = string
boot_time_in_minutes = number
maximum_count = number
})
github = object({
organization_runners = bool
enterprise_server = object({
url = optional(string, null)
ssl_verify = bool
})
user_agent = optional(string, null)
app_parameters = object({
key_base64 = list(map(string))
id = list(map(string))
installation_id = list(object({ name = string, arn = string }))
})
})
queue = object({
build = object({
arn = string
})
kms_key_id = optional(string, null)
event_source_mapping = object({
batch_size = number
maximum_batching_window_in_seconds = number
})
})
ssm = object({
token_path = string
token_path_arn = string
config_path = string
config_path_arn = string
parameter_store_tags = string
kms_key_id = optional(string, null)
})
observability = object({
logs = object({
level = string
retention_in_days = number
kms_key_id = optional(string, null)
class = string
})
tracing = object({
mode = optional(string, null)
capture_http_requests = bool
capture_error = bool
})
metrics = object({
enabled = bool
namespace = string
metric = object({
github_app_rate_limit = object({
enabled = bool
})
})
})
})
scale_up = object({
memory_size = number
timeout = number
reserved_concurrent_executions = number
job_queued_check_enabled = bool
tags = object({
resources = map(string)
lambda = map(string)
log_group = map(string)
event_source_mapping = map(string)
})
})
scale_down = object({
memory_size = number
timeout = number
schedule_expression = string
minimum_running_time_in_minutes = optional(number, null)
idle_config = list(object({
cron = string
timeZone = string
idleCount = number
evictionStrategy = string
}))
tags = object({
resources = map(string)
lambda = map(string)
log_group = map(string)
})
})
job_retry = object({
enabled = bool
max_attempts = number
delay_in_seconds = number
delay_backoff = number
queue = optional(object({
arn = string
url = string
}), null)
})
}) | n/a | yes |
| [runner\_provider](#input\_runner\_provider) | Selected compute-provider integration for the scaling control plane.object({
type = string
scale_up = object({
environment_variables = map(string)
iam_policy_json = string
additional_iam_policy_json = optional(string, null)
managed_policy = optional(object({
arn = string
}), null)
})
scale_down = object({
environment_variables = map(string)
iam_policy_json = string
})
}) | n/a | yes |
## Outputs
diff --git a/modules/orchestration-providers/webhook/scale-runners/variables.tf b/modules/orchestration-providers/webhook/scale-runners/variables.tf
index eb4ef2dddf..e191e303d1 100644
--- a/modules/orchestration-providers/webhook/scale-runners/variables.tf
+++ b/modules/orchestration-providers/webhook/scale-runners/variables.tf
@@ -112,7 +112,7 @@ variable "config" {
app_parameters = object({
key_base64 = list(map(string))
id = list(map(string))
- installation_id = optional(list(object({ name = string, arn = string })), [null])
+ installation_id = list(object({ name = string, arn = string }))
})
})
queue = object({
diff --git a/modules/orchestration-providers/webhook/variables.tf b/modules/orchestration-providers/webhook/variables.tf
index 83bd6f30e6..5dfecdbd6c 100644
--- a/modules/orchestration-providers/webhook/variables.tf
+++ b/modules/orchestration-providers/webhook/variables.tf
@@ -169,7 +169,7 @@ variable "github" {
app_parameters = object({
key_base64 = list(map(string))
id = list(map(string))
- installation_id = optional(list(object({ name = string, arn = string })), [null])
+ installation_id = list(object({ name = string, arn = string }))
})
enterprise_server = object({
url = optional(string, null)
diff --git a/modules/runner-config/README.md b/modules/runner-config/README.md
index 76f5489018..0ef04fab05 100644
--- a/modules/runner-config/README.md
+++ b/modules/runner-config/README.md
@@ -111,7 +111,7 @@ yarn run dist
| [aws\_region](#input\_aws\_region) | AWS region. | `string` | n/a | yes |
| [compute\_provider](#input\_compute\_provider) | Typed compute-provider configuration. Provider-owned settings remain inside the selected compute-provider block.object({
aws = optional(object({
ec2 = optional(object({
ami = optional(object({
filter = optional(map(list(string)), { state = ["available"] })
owners = optional(list(string), ["amazon"])
id_ssm_parameter = optional(object({
arn = string
}), null)
kms_key = optional(object({
arn = string
}), null)
}), null)
vpc_id = string
subnet_ids = list(string)
overrides = optional(object({
name_runner = optional(string, "")
name_sg = optional(string, "")
}), {})
instance_profile = optional(object({
name = string
}), null)
instance_profile_path = optional(string, null)
binaries_syncer = optional(object({
enabled = optional(bool, true)
s3 = optional(object({
arn = string
id = string
key = string
}), null)
}), {})
block_device_mappings = optional(list(object({
delete_on_termination = optional(bool, true)
device_name = optional(string, "/dev/xvda")
encrypted = optional(bool, true)
iops = optional(number)
kms_key_id = optional(string)
snapshot_id = optional(string)
throughput = optional(number)
volume_initialization_rate = optional(number)
volume_size = number
volume_type = optional(string, "gp3")
})), [{ volume_size = 30 }])
ebs_optimized = optional(bool, false)
instance_target_capacity_type = optional(string, "spot")
instance_allocation_strategy = optional(string, "lowest-price")
instance_type_priorities = optional(map(number), null)
instance_max_spot_price = optional(string, null)
instance_types = list(string)
user_data = optional(object({
enabled = optional(bool, true)
template = optional(string, null)
content = optional(string, null)
pre_install = optional(string, "")
post_install = optional(string, "")
debug_logging_enabled = optional(bool, false)
}), {})
ssm_enabled = optional(bool, false)
create_service_linked_role_spot = optional(bool, false)
cloudwatch_agent = optional(object({
enabled = optional(bool, true)
config = optional(string, null)
}), {})
managed_security_group_enabled = optional(bool, true)
log_files = optional(list(object({
log_group_name = string
prefix_log_group = bool
file_path = string
log_stream_name = string
log_class = optional(string, "STANDARD")
})), null)
key_name = optional(string, null)
additional_security_group_ids = optional(list(string), [])
detailed_monitoring_enabled = optional(bool, false)
egress_rules = optional(list(object({
cidr_blocks = list(string)
ipv6_cidr_blocks = list(string)
prefix_list_ids = list(string)
from_port = number
protocol = string
security_groups = list(string)
self = bool
to_port = number
description = string
})), [{
cidr_blocks = ["0.0.0.0/0"]
ipv6_cidr_blocks = ["::/0"]
prefix_list_ids = null
from_port = 0
protocol = "-1"
security_groups = null
self = null
to_port = 0
description = null
}])
tags = optional(map(string), {})
metadata_options = optional(object({
instance_metadata_tags = optional(string, "enabled")
http_endpoint = optional(string, "enabled")
http_tokens = optional(string, "required")
http_put_response_hop_limit = optional(number, 1)
}), {})
credit_specification = optional(string, null)
cpu_options = optional(object({
core_count = optional(number)
threads_per_core = optional(number)
amd_sev_snp = optional(string)
nested_virtualization = optional(string)
}), null)
placement = optional(object({
affinity = optional(string)
availability_zone = optional(string)
group_id = optional(string)
group_name = optional(string)
host_id = optional(string)
host_resource_group_arn = optional(string)
spread_domain = optional(string)
tenancy = optional(string)
partition_number = optional(number)
}), null)
license_specifications = optional(list(object({
license_configuration_arn = string
})), [])
associate_public_ipv4_address = optional(bool, false)
on_demand_failover_for_errors = optional(list(string), [])
scale_errors = optional(list(string), [
"UnfulfillableCapacity",
"MaxSpotInstanceCountExceeded",
"TargetCapacityLimitExceededException",
"RequestLimitExceeded",
"ResourceLimitExceeded",
"MaxSpotInstanceCountExceeded",
"MaxSpotFleetRequestCountExceeded",
"InsufficientInstanceCapacity",
"InsufficientCapacityOnHost",
])
use_dedicated_host = optional(bool, false)
}), null)
}), {})
}) | n/a | yes |
| [compute\_provider\_key](#input\_compute\_provider\_key) | Optional plan-known compute-provider dispatch key. Null discovers the key from the exactly one populated compute\_provider block. | `string` | `null` | no |
-| [github](#input\_github) | GitHub API and runner-registration configuration.object({
app_parameters = object({
key_base64 = list(map(string))
id = list(map(string))
installation_id = optional(list(object({ name = string, arn = string })), [null])
})
enterprise_server = optional(object({
url = optional(string, null)
ssl_verify = optional(bool, true)
}), {})
user_agent = optional(string, null)
}) | n/a | yes |
+| [github](#input\_github) | GitHub API and runner-registration configuration.object({
app_parameters = object({
key_base64 = list(map(string))
id = list(map(string))
installation_id = list(object({ name = string, arn = string }))
})
enterprise_server = optional(object({
url = optional(string, null)
ssl_verify = optional(bool, true)
}), {})
user_agent = optional(string, null)
}) | n/a | yes |
| [lambda](#input\_lambda) | Common Lambda substrate independent of the selected runner orchestration provider.object({
artifact = optional(object({
s3 = optional(object({
bucket = optional(string, null)
}), {})
}), {})
runtime = optional(string, "nodejs24.x")
architecture = optional(string, "arm64")
subnet_ids = optional(list(string), [])
security_group_ids = optional(list(string), [])
tags = optional(map(string), {})
principals = optional(list(object({
type = string
identifiers = list(string)
})), [])
role = optional(object({
path = optional(string, null)
permissions_boundary = optional(string, null)
}), {})
}) | `{}` | no |
| [observability](#input\_observability) | Logging, tracing, and metrics configuration for control-plane and provider resources.object({
logs = optional(object({
level = optional(string, "info")
retention_in_days = optional(number, 180)
kms_key_id = optional(string, null)
class = optional(string, "STANDARD")
tags = optional(map(string), {})
}), {})
tracing = optional(object({
mode = optional(string, null)
capture_http_requests = optional(bool, false)
capture_error = optional(bool, false)
}), {})
metrics = optional(object({
enabled = optional(bool, false)
namespace = optional(string, "GitHub Runners")
metric = optional(object({
github_app_rate_limit = optional(object({
enabled = optional(bool, true)
}), {})
job_retry = optional(object({
enabled = optional(bool, true)
}), {})
spot_termination_warning = optional(object({
enabled = optional(bool, true)
}), {})
}), {})
}), {})
}) | `{}` | no |
| [orchestration\_provider](#input\_orchestration\_provider) | Runner demand-orchestration provider configuration. Exactly one provider block must be non-null. Wrapper presence selects the provider and must therefore be known during planning; values inside the selected provider may remain unknown until apply.object({
webhook = optional(object({
runner = optional(object({
boot_time_in_minutes = optional(number, 5)
ephemeral = optional(bool, false)
jit_config_enabled = optional(bool, null)
maximum_count = optional(number, 3)
}), {})
github = object({
organization_runners = bool
})
queue = object({
build = object({
arn = string
url = string
})
kms_key_id = optional(string, null)
tags = optional(map(string), {})
})
lambda = optional(object({
artifact = optional(object({
zip = optional(string, null)
s3 = optional(object({
key = string
object_version = optional(string, null)
}), null)
}), {})
scale = optional(object({
up = optional(object({
memory_size = optional(number, 512)
timeout = optional(number, 60)
reserved_concurrent_executions = optional(number, 1)
job_queued_check_enabled = optional(bool, null)
event_source_mapping = optional(object({
batch_size = optional(number, 10)
maximum_batching_window_in_seconds = optional(number, 0)
}), {})
tags = optional(map(string), {})
}), {})
down = optional(object({
memory_size = optional(number, 512)
timeout = optional(number, 60)
schedule_expression = optional(string, "cron(*/5 * * * ? *)")
minimum_running_time_in_minutes = optional(number, null)
idle_config = optional(list(object({
cron = string
timeZone = string
idleCount = number
evictionStrategy = optional(string, "oldest_first")
})), [])
tags = optional(map(string), {})
}), {})
}), {})
pool = optional(object({
memory_size = optional(number, 512)
timeout = optional(number, 60)
reserved_concurrent_executions = optional(number, 1)
config = optional(list(object({
schedule_expression = string
schedule_expression_timezone = optional(string)
size = number
})), [])
include_busy_runners = optional(bool, false)
runner_owner = optional(string, null)
tags = optional(map(string), {})
}), {})
}), {})
job_retry = optional(object({
enabled = optional(bool, false)
delay_in_seconds = optional(number, 300)
delay_backoff = optional(number, 2)
max_attempts = optional(number, 1)
tags = optional(map(string), {})
lambda = optional(object({
memory_size = optional(number, 256)
reserved_concurrent_executions = optional(number, 1)
timeout = optional(number, 30)
}), {})
}), {})
}), null)
scale_set = optional(object({
github = object({
config_url = string
installation_id_ssm = object({
name = string
arn = string
kms_key_arn = optional(string, null)
})
force_ghes = optional(bool, null)
})
name = string
id = number
runner_group_id = optional(number, null)
min_runners = optional(number, 0)
max_runners = optional(number, 10)
boot_time_in_minutes = optional(number, 10)
session_owner = optional(string, null)
work_folder = optional(string, null)
}), null)
}) | n/a | yes |
diff --git a/modules/runner-config/variables.tf b/modules/runner-config/variables.tf
index 82cd9eaead..5928693ade 100644
--- a/modules/runner-config/variables.tf
+++ b/modules/runner-config/variables.tf
@@ -86,7 +86,7 @@ variable "github" {
app_parameters = object({
key_base64 = list(map(string))
id = list(map(string))
- installation_id = optional(list(object({ name = string, arn = string })), [null])
+ installation_id = list(object({ name = string, arn = string }))
})
enterprise_server = optional(object({
url = optional(string, null)
diff --git a/tests/ministack/README.md b/tests/ministack/README.md
index f0e6440fca..a64f44f134 100644
--- a/tests/ministack/README.md
+++ b/tests/ministack/README.md
@@ -1,8 +1,7 @@
# MiniStack example tests
The MiniStack workflow runs the `base`, `prebuilt`, `default`, `ephemeral`,
-`multi-runner`, `multi-runner-v2`, `multi-runner-scale-set`, and
-`termination-watcher` examples directly
+`multi-runner`, `multi-runner-v2`, and `termination-watcher` examples directly
with Terraform 1.4.0 and the latest Terraform release.
The examples with input variables get their inputs from their own tfvars files
in this directory. The `termination-watcher` example has no input variables
@@ -10,7 +9,7 @@ and uses the configuration checked into the example itself. No override files,
setup module, or Terraform fixture configuration is checked in. The helper
creates and removes a temporary AMI override for `default` and
`ephemeral`, temporary SSM parameters for `multi-runner`, and temporary AMI
-fixtures plus an override for `multi-runner-v2` and `multi-runner-scale-set`.
+fixtures for `multi-runner-v2`.
Start MiniStack, set the AWS endpoint and test credentials, then run:
@@ -27,8 +26,6 @@ tests/ministack/run-example.sh apply multi-runner
# or
tests/ministack/run-example.sh apply multi-runner-v2
# or
-tests/ministack/run-example.sh apply multi-runner-scale-set
-# or
tests/ministack/run-example.sh apply termination-watcher
```
@@ -37,5 +34,5 @@ ZIP fixtures in the paths expected by the modules when they are absent, and
removes only the files it created. For `prebuilt`, it seeds AMI metadata through
MiniStack's AWS-compatible EC2 API, then removes only the resources it created
during cleanup. MiniStack v1.5.7 provides the EC2 image behavior needed by the
-`default`, `ephemeral`, `multi-runner`, and `multi-runner-scale-set` examples,
-so they are included in the same lifecycle matrix.
+`default`, `ephemeral`, and `multi-runner` examples, so they are included in
+the same lifecycle matrix.
diff --git a/tests/ministack/multi-runner-scale-set.tfvars b/tests/ministack/multi-runner-scale-set.tfvars
deleted file mode 100644
index c186656fad..0000000000
--- a/tests/ministack/multi-runner-scale-set.tfvars
+++ /dev/null
@@ -1,51 +0,0 @@
-environment = "ministack-scale-set"
-aws_region = "eu-west-1"
-
-github_app = {
- id = "0"
- key_base64 = "ministack-invalid-key"
-}
-
-runner_binaries_enabled = false
-
-ami = {
- "linux-arm64" = {
- filter = {
- name = ["ministack-scale-set-linux-arm64"]
- state = ["available"]
- }
- owners = ["self"]
- }
- "linux-x64" = {
- filter = {
- name = ["ministack-scale-set-linux-x64"]
- state = ["available"]
- }
- owners = ["self"]
- }
- "linux-scale-set" = {
- filter = {
- name = ["ministack-scale-set-linux-x64"]
- state = ["available"]
- }
- owners = ["self"]
- }
- "windows-x64" = {
- filter = {
- name = ["ministack-scale-set-windows-x64"]
- state = ["available"]
- }
- owners = ["self"]
- }
-}
-
-scale_set = {
- config_url = "https://github.com/example"
- installation_id_ssm = {
- name = "/ministack/scale-set/installation-id"
- arn = "arn:aws:ssm:eu-west-1:000000000000:parameter/ministack/scale-set/installation-id"
- }
- name = "ministack-scale-set"
- id = 1
- runner_group_id = 1
-}
diff --git a/tests/ministack/multi-runner-v2.tfvars b/tests/ministack/multi-runner-v2.tfvars
index de54e291cf..0f9c6073fc 100644
--- a/tests/ministack/multi-runner-v2.tfvars
+++ b/tests/ministack/multi-runner-v2.tfvars
@@ -6,8 +6,6 @@ github_app = {
key_base64 = "ministack-invalid-key"
}
-runner_binaries_enabled = false
-
ami = {
"linux-arm64" = {
filter = {
diff --git a/tests/ministack/run-example.sh b/tests/ministack/run-example.sh
index 2545473d6c..8fd5a41bef 100755
--- a/tests/ministack/run-example.sh
+++ b/tests/ministack/run-example.sh
@@ -14,14 +14,14 @@ example="${2:-}"
tfvars_file="${3:-${MINISTACK_TFVARS_FILE:-}}"
case "$example" in
- base | prebuilt | default | ephemeral | multi-runner | multi-runner-v2 | multi-runner-scale-set)
+ base | prebuilt | default | ephemeral | multi-runner | multi-runner-v2)
use_tfvars=true
;;
termination-watcher)
use_tfvars=false
;;
*)
- echo "Supported examples for the runner are: base, prebuilt, default, ephemeral, multi-runner, multi-runner-v2, multi-runner-scale-set, termination-watcher" >&2
+ echo "Supported examples for the runner are: base, prebuilt, default, ephemeral, multi-runner, multi-runner-v2, termination-watcher" >&2
exit 64
;;
esac
@@ -29,7 +29,7 @@ esac
case "$action" in
init | plan | apply | destroy) ;;
*)
- echo "Usage: $0 {init|plan|apply|destroy} {base|prebuilt|default|ephemeral|multi-runner|multi-runner-v2|multi-runner-scale-set|termination-watcher} [TFVARS_FILE]" >&2
+ echo "Usage: $0 {init|plan|apply|destroy} {base|prebuilt|default|ephemeral|multi-runner|multi-runner-v2|termination-watcher} [TFVARS_FILE]" >&2
exit 64
;;
esac
@@ -250,14 +250,6 @@ $lambda_zip"
create_ami_fixture "ministack-v2-linux-x64" x86_64 >/dev/null
create_ami_fixture "ministack-v2-windows-x64" x86_64 >/dev/null
;;
- multi-runner-scale-set)
- create_ami_fixture "ministack-scale-set-linux-x64" x86_64 >/dev/null
- create_ami_fixture "ministack-scale-set-linux-arm64" arm64 >/dev/null
- create_ami_fixture "ministack-scale-set-windows-x64" x86_64 >/dev/null
- create_ssm_fixture \
- "/ministack/scale-set/installation-id" \
- "1"
- ;;
esac
}