Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 40 additions & 8 deletions modules/platform/ec2_deployment/README.md

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ locals {
}

module "ec2_update_runner_ssm_ami" {
count = length(local.ec2_runner_configs) > 0 ? 1 : 0
source = "./ec2_update_runner_ssm_ami"

providers = {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
module "ec2_update_runner_tags" {
count = length(local.ec2_runner_configs) > 0 ? 1 : 0
source = "./ec2_update_runner_tags"

providers = {
Expand Down
18 changes: 14 additions & 4 deletions modules/platform/ec2_deployment/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ locals {
terraform_aws_github_runner_tags = merge(
var.tenant_configs.tags,
{
terraform-aws-github-runner-ref = "v7.10.1"
terraform-aws-github-runner-ref = "microvm-multi-runner-terraform"
}
)
webhook_api_gateway_access_log_format = jsonencode({
Expand Down Expand Up @@ -67,7 +67,13 @@ data "aws_subnet" "runner_subnet" {
}

data "external" "download_lambdas" {
program = ["bash", "${path.module}/scripts/download_lambdas.sh", "/tmp/${var.runner_configs.prefix}/", "v7.10.1", "github-aws-runners/terraform-aws-github-runner"]
count = (
length(local.ec2_runner_configs) > 0
|| try(var.runner_configs.lambda_artifacts.control_plane_zip, null) == null
|| try(var.runner_configs.lambda_artifacts.webhook_zip, null) == null
) ? 1 : 0

program = ["bash", "${path.module}/scripts/download_lambdas.sh", "/tmp/${var.runner_configs.prefix}/v7.10.1/", "v7.10.1", "github-aws-runners/terraform-aws-github-runner"]
}

# ---------------------------------------------------------------------------
Expand Down Expand Up @@ -105,6 +111,8 @@ resource "aws_ssm_parameter" "hook_job_completed" {
}

data "aws_iam_policy_document" "runner_hooks_ssm_read" {
count = length(local.ec2_runner_configs) > 0 ? 1 : 0

statement {
sid = "ReadRunnerHookParameters"
effect = "Allow"
Expand All @@ -117,16 +125,18 @@ data "aws_iam_policy_document" "runner_hooks_ssm_read" {
}

resource "aws_iam_policy" "runner_hooks_ssm_read" {
count = length(local.ec2_runner_configs) > 0 ? 1 : 0
name = "${var.runner_configs.prefix}-runner-hooks-ssm-read"
path = "/"
description = "Allow runners to read their gzip'd job-hook scripts from SSM."
policy = data.aws_iam_policy_document.runner_hooks_ssm_read.json
policy = data.aws_iam_policy_document.runner_hooks_ssm_read[0].json
tags = var.tenant_configs.tags
}


module "runners" {
#checkov:skip=CKV_TF_1:Temporary upstream experimental ref is required to validate multi-runner v2 compatibility before a release tag exists.
source = "git::https://github.com/github-aws-runners/terraform-aws-github-runner.git//modules/multi-runner?ref=experimental-multi-runner-config-v2-20260805"
source = "git::https://github.com/github-aws-runners/terraform-aws-github-runner.git//modules/multi-runner?ref=microvm-multi-runner-terraform"

aws_region = var.aws_region

Expand Down
19 changes: 19 additions & 0 deletions modules/platform/ec2_deployment/moved.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
moved {
from = aws_iam_policy.ec2_tags
to = aws_iam_policy.ec2_tags[0]
}

moved {
from = aws_iam_policy.runner_hooks_ssm_read
to = aws_iam_policy.runner_hooks_ssm_read[0]
}

moved {
from = module.ec2_update_runner_ssm_ami
to = module.ec2_update_runner_ssm_ami[0]
}

moved {
from = module.ec2_update_runner_tags
to = module.ec2_update_runner_tags[0]
}
44 changes: 42 additions & 2 deletions modules/platform/ec2_deployment/outputs.tf
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,25 @@ output "webhook_endpoint" {

output "ec2_runners_arn_map" {
value = {
for runner_key, runner in module.runners.runners_map_v2 : runner_key => coalesce(
try(runner.runner.role.arn, null),
for runner_key in keys(local.ec2_runner_configs) : runner_key => coalesce(
try(module.runners.runners_map_v2[runner_key].runner.role.arn, null),
try(local.ec2_runner_configs[runner_key].runner.iam.role.arn, null),
)
}
description = "Map of EC2 runner keys to their IAM role ARNs."
}

output "runners_arn_map" {
value = {
for runner_key, runner in module.runners.runners_map_v2 : runner_key => coalesce(
try(runner.runner.role.arn, null),
try(runner.provider.aws.microvm.execution_role_arn, null),
try(local.runner_configs[runner_key].runner.iam.role.arn, null),
)
}
description = "Map of runner keys to their resolved EC2 or Lambda MicroVM execution-role ARNs."
}

output "ec2_runners_ami_name_map" {
value = {
for runner_key in keys(local.ec2_runner_configs) : runner_key => data.aws_ami.runner_ami[runner_key].name
Expand All @@ -27,6 +38,35 @@ output "ec2_runners_labels_map" {
description = "Map of EC2 runner keys to their base and extra GitHub labels."
}

output "runners_labels_map" {
value = local.runner_labels
description = "Map of runner keys to their base and extra GitHub labels."
}

output "microvm_runners_arn_map" {
value = {
for runner_key in keys(local.microvm_runner_configs) :
runner_key => module.runners.runners_map_v2[runner_key].provider.aws.microvm.execution_role_arn
}
description = "Map of Lambda MicroVM runner keys to their execution-role ARNs."
}

output "microvm_runners_labels_map" {
value = {
for runner_key in keys(local.microvm_runner_configs) :
runner_key => local.runner_labels[runner_key]
}
description = "Map of Lambda MicroVM runner keys to their base and extra GitHub labels."
}

output "microvm_runners_map" {
value = {
for runner_key in keys(local.microvm_runner_configs) :
runner_key => module.runners.runners_map_v2[runner_key].provider.aws.microvm
}
description = "Map of Lambda MicroVM runner keys to their provider-owned image, execution-role, and runtime log-group outputs."
}

output "subnet_cidr_blocks" {
value = { for id, subnet in data.aws_subnet.runner_subnet : id => subnet.cidr_block }
description = "Map of EC2 runner subnet IDs to their CIDR blocks."
Expand Down
2 changes: 2 additions & 0 deletions modules/platform/ec2_deployment/roles.tf
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,9 @@ data "aws_iam_policy_document" "ec2_tags" {

# Define the actual IAM policy for EC2 tags
resource "aws_iam_policy" "ec2_tags" {
count = length(local.ec2_runner_configs) > 0 ? 1 : 0
name = "${var.runner_configs.prefix}-policy-for-ec2-tags"
path = "/"
description = "Policy that allows EC2 instances to create tags on themselves."
policy = data.aws_iam_policy_document.ec2_tags.json

Expand Down
90 changes: 68 additions & 22 deletions modules/platform/ec2_deployment/runner_configs_v2.tf
Original file line number Diff line number Diff line change
@@ -1,5 +1,39 @@
locals {
ec2_runner_configs = var.runner_configs.runner_specs
runner_configs = var.runner_configs.runner_specs
runner_binaries_default_enabled = true
runner_iam_policy_arn_prefix = "arn:${data.aws_partition.current.partition}:iam::${data.aws_caller_identity.current.account_id}:policy"
runner_binaries_targets = {
for target in distinct([
for runner_config in values(var.runner_configs.runner_specs) : {
os = runner_config.runner.os
architecture = runner_config.runner.architecture
}
if(
length(try(runner_config.compute_provider.aws.ec2[*], [])) == 1
&& coalesce(
try(runner_config.compute_provider.aws.ec2.binaries_syncer.enabled, null),
local.runner_binaries_default_enabled,
)
)
]) : "${target.os}_${target.architecture}" => target
}
compute_provider_selections = {
for runner_key, runner_config in var.runner_configs.runner_specs : runner_key => {
namespace = "aws"
type = (
length(try(runner_config.compute_provider.aws.ec2[*], [])) == 1 ? "ec2" :
length(try(runner_config.compute_provider.aws.microvm[*], [])) == 1 ? "microvm" : null
)
}
}
ec2_runner_configs = {
for key, runner_config in local.runner_configs : key => runner_config
if length(try(runner_config.compute_provider.aws.ec2[*], [])) == 1
}
microvm_runner_configs = {
for key, runner_config in local.runner_configs : key => runner_config
if length(try(runner_config.compute_provider.aws.microvm[*], [])) == 1
}

effective_runner_users = {
for key, runner_config in local.ec2_runner_configs :
Expand Down Expand Up @@ -89,7 +123,7 @@ locals {
}

legacy_runner_labels = {
for key, runner_config in local.ec2_runner_configs :
for key, runner_config in local.runner_configs :
key => concat(
try(runner_config.orchestration_provider.webhook.matcherConfig.labelMatchers[0], []),
coalesce(runner_config.runner.extra_labels, []),
Expand All @@ -100,7 +134,7 @@ locals {
# only new labels introduced by additional v2 matchers. The upstream v1 module
# separately sorts the corresponding set before registering runners.
runner_labels = {
for key, runner_config in local.ec2_runner_configs :
for key, runner_config in local.runner_configs :
key => concat(
local.legacy_runner_labels[key],
distinct([
Expand Down Expand Up @@ -181,21 +215,21 @@ locals {
}
)
log_files = coalesce(runner_config.compute_provider.aws.ec2.log_files, local.forge_ec2_log_files[key])
tags = merge(var.tenant_configs.tags, runner_config.compute_provider.aws.ec2.tags)
tags = runner_config.compute_provider.aws.ec2.tags
}
)
}

# Preserve Forge-managed runner hooks, policies, bootstrap content, logging,
# and tags while passing the public nested contract directly to upstream v2.
# Preserve the provider-neutral lane contract while applying Forge's EC2
# bootstrap, lifecycle-hook, and policy overlays only to EC2 lanes.
multi_runner_config = {
for key, runner_config in local.ec2_runner_configs :
for key, runner_config in local.runner_configs :
key => merge(runner_config, {
runner = merge(runner_config.runner, {
hooks = {
hooks = contains(keys(local.ec2_runner_configs), key) ? {
job_started = local.runner_hook_job_started[key]
job_completed = local.runner_hook_job_completed[key]
}
} : runner_config.runner.hooks
iam = merge(runner_config.runner.iam, {
managed_policy_arns = merge(
coalesce(runner_config.runner.iam.managed_policy_arns, {}),
Expand All @@ -204,22 +238,31 @@ locals {
for policy_index, policy_arn in var.runner_configs.runner_iam_role_managed_policy_arns :
"forge-config-${policy_index}" => policy_arn
},
{
forge_ec2_tags = aws_iam_policy.ec2_tags.arn
forge_runner_hooks_ssm_read = aws_iam_policy.runner_hooks_ssm_read.arn
},
contains(keys(local.ec2_runner_configs), key) ? {
forge_ec2_tags = "${local.runner_iam_policy_arn_prefix}${aws_iam_policy.ec2_tags[0].path}${aws_iam_policy.ec2_tags[0].name}"
forge_runner_hooks_ssm_read = "${local.runner_iam_policy_arn_prefix}${aws_iam_policy.runner_hooks_ssm_read[0].path}${aws_iam_policy.runner_hooks_ssm_read[0].name}"
} : {},
) : {},
)
})
})
compute_provider = {
aws = {
ec2 = local.ec2_compute_provider[key]
}
}
compute_provider = merge(runner_config.compute_provider, {
aws = merge(runner_config.compute_provider.aws, {
ec2 = try(local.ec2_compute_provider[key], null)
})
})
})
}

control_plane_lambda_zip = try(coalesce(
try(var.runner_configs.lambda_artifacts.control_plane_zip, null),
try("${data.external.download_lambdas[0].result.path}/runners.zip", null),
), null)
webhook_lambda_zip = try(coalesce(
try(var.runner_configs.lambda_artifacts.webhook_zip, null),
try("${data.external.download_lambdas[0].result.path}/webhook.zip", null),
), null)

experimental_config = {
tags = local.terraform_aws_github_runner_tags

Expand All @@ -243,11 +286,11 @@ locals {
}
lambda = {
artifact = {
zip = "${data.external.download_lambdas.result.path}/runners.zip"
zip = local.control_plane_lambda_zip
}
webhook = {
artifact = {
zip = "${data.external.download_lambdas.result.path}/webhook.zip"
zip = local.webhook_lambda_zip
}
api_gateway_access_log_settings = {
destination_arn = aws_cloudwatch_log_group.webhook_api_gateway_access.arn
Expand All @@ -266,7 +309,7 @@ locals {
housekeeper = {
lambda = {
artifact = {
zip = "${data.external.download_lambdas.result.path}/runners.zip"
zip = local.control_plane_lambda_zip
}
}
}
Expand All @@ -280,14 +323,17 @@ locals {
}

compute_provider = {
selections = local.compute_provider_selections
aws = {
ec2 = {
vpc_id = var.network_configs.vpc_id
subnet_ids = var.network_configs.subnet_ids
runner_binaries = {
enabled = local.runner_binaries_default_enabled
targets = local.runner_binaries_targets
syncer = {
artifact = {
zip = "${data.external.download_lambdas.result.path}/runner-binaries-syncer.zip"
zip = try("${data.external.download_lambdas[0].result.path}/runner-binaries-syncer.zip", null)
}
}
}
Expand Down
Loading