From fe00fd3cb1d793d5ece3439a198c762d11bb0cb1 Mon Sep 17 00:00:00 2001 From: Marat Soltobaev Date: Thu, 6 Nov 2025 12:35:53 -0600 Subject: [PATCH 1/3] feat(runner-role): Enable using separate iam role for runners --- main.tf | 1 + modules/runners/logging.tf | 4 +-- modules/runners/main.tf | 2 +- modules/runners/policies-runner.tf | 41 +++++++++++++++++------------- modules/runners/pool.tf | 2 +- modules/runners/scale-up.tf | 2 +- modules/runners/variables.tf | 19 +++++++++++++- variables.tf | 17 +++++++++++++ 8 files changed, 64 insertions(+), 24 deletions(-) diff --git a/main.tf b/main.tf index 9c72614808..f776f70ee1 100644 --- a/main.tf +++ b/main.tf @@ -157,6 +157,7 @@ module "runners" { subnet_ids = var.subnet_ids prefix = var.prefix tags = local.tags + iam_overrides = var.iam_overrides ssm_paths = { root = local.ssm_root_path diff --git a/modules/runners/logging.tf b/modules/runners/logging.tf index 1b61f16f7b..84823bb432 100644 --- a/modules/runners/logging.tf +++ b/modules/runners/logging.tf @@ -59,9 +59,9 @@ resource "aws_cloudwatch_log_group" "gh_runners" { } resource "aws_iam_role_policy" "cloudwatch" { - count = var.enable_cloudwatch_agent ? 1 : 0 + count = var.iam_overrides["override_runner_role"] ? 0 : (var.enable_cloudwatch_agent ? 1 : 0) name = "CloudWatchLogginAndMetrics" - role = aws_iam_role.runner.name + role = aws_iam_role.runner[0].name policy = templatefile("${path.module}/policies/instance-cloudwatch-policy.json", { ssm_parameter_arn = aws_ssm_parameter.cloudwatch_agent_config_runner[0].arn diff --git a/modules/runners/main.tf b/modules/runners/main.tf index 7184b08ee0..2966d2522b 100644 --- a/modules/runners/main.tf +++ b/modules/runners/main.tf @@ -173,7 +173,7 @@ resource "aws_launch_template" "runner" { } iam_instance_profile { - name = aws_iam_instance_profile.runner.name + name = var.iam_overrides["override_instance_profile"] ? var.iam_overrides["instance_profile_name"] : aws_iam_instance_profile.runner[0].name } instance_initiated_shutdown_behavior = "terminate" diff --git a/modules/runners/policies-runner.tf b/modules/runners/policies-runner.tf index 2b7d894619..405245b0aa 100644 --- a/modules/runners/policies-runner.tf +++ b/modules/runners/policies-runner.tf @@ -1,6 +1,7 @@ data "aws_caller_identity" "current" {} resource "aws_iam_role" "runner" { + count = var.iam_overrides["override_runner_role"] ? 0 : 1 name = "${substr("${var.prefix}-runner", 0, 54)}-${substr(md5("${var.prefix}-runner"), 0, 8)}" assume_role_policy = templatefile("${path.module}/policies/instance-role-trust-policy.json", {}) path = local.role_path @@ -9,22 +10,24 @@ resource "aws_iam_role" "runner" { } resource "aws_iam_instance_profile" "runner" { - name = "${var.prefix}-runner-profile" - role = aws_iam_role.runner.name - path = local.instance_profile_path - tags = local.tags + count = var.iam_overrides["override_instance_profile"] ? 0 : 1 + name = "${var.prefix}-runner-profile" + role = aws_iam_role.runner[0].name + path = local.instance_profile_path + tags = local.tags } resource "aws_iam_role_policy" "runner_session_manager_aws_managed" { + count = var.iam_overrides["override_runner_role"] ? 0 : (var.enable_ssm_on_runners ? 1 : 0) name = "runner-ssm-session" - count = var.enable_ssm_on_runners ? 1 : 0 - role = aws_iam_role.runner.name + role = aws_iam_role.runner[0].name policy = templatefile("${path.module}/policies/instance-ssm-policy.json", {}) } resource "aws_iam_role_policy" "ssm_parameters" { - name = "runner-ssm-parameters" - role = aws_iam_role.runner.name + count = var.iam_overrides["override_runner_role"] ? 0 : 1 + name = "runner-ssm-parameters" + role = aws_iam_role.runner[0].name policy = templatefile("${path.module}/policies/instance-ssm-parameters-policy.json", { arn_ssm_parameters_path_tokens = "arn:${var.aws_partition}:ssm:${var.aws_region}:${data.aws_caller_identity.current.account_id}:parameter${var.ssm_paths.root}/${var.ssm_paths.tokens}" @@ -34,10 +37,10 @@ resource "aws_iam_role_policy" "ssm_parameters" { } resource "aws_iam_role_policy" "dist_bucket" { - count = var.enable_runner_binaries_syncer ? 1 : 0 + count = var.iam_overrides["override_runner_role"] ? 0 : (var.enable_runner_binaries_syncer ? 1 : 0) name = "distribution-bucket" - role = aws_iam_role.runner.name + role = aws_iam_role.runner[0].name policy = templatefile("${path.module}/policies/instance-s3-policy.json", { s3_arn = "${var.s3_runner_binaries.arn}/${var.s3_runner_binaries.key}" @@ -46,33 +49,35 @@ resource "aws_iam_role_policy" "dist_bucket" { } resource "aws_iam_role_policy_attachment" "xray_tracing" { - count = var.tracing_config.mode != null ? 1 : 0 - role = aws_iam_role.runner.name + count = var.iam_overrides["override_runner_role"] ? 0 : (var.tracing_config.mode != null ? 1 : 0) + role = aws_iam_role.runner[0].name policy_arn = "arn:${var.aws_partition}:iam::aws:policy/AWSXRayDaemonWriteAccess" } resource "aws_iam_role_policy" "describe_tags" { + count = var.iam_overrides["override_runner_role"] ? 0 : 1 name = "runner-describe-tags" - role = aws_iam_role.runner.name + role = aws_iam_role.runner[0].name policy = file("${path.module}/policies/instance-describe-tags-policy.json") } resource "aws_iam_role_policy" "create_tag" { + count = var.iam_overrides["override_runner_role"] ? 0 : 1 name = "runner-create-tags" - role = aws_iam_role.runner.name + role = aws_iam_role.runner[0].name policy = templatefile("${path.module}/policies/instance-create-tags-policy.json", {}) } resource "aws_iam_role_policy_attachment" "managed_policies" { - count = length(var.runner_iam_role_managed_policy_arns) - role = aws_iam_role.runner.name + count = var.iam_overrides["override_runner_role"] ? 0 : length(var.runner_iam_role_managed_policy_arns) + role = aws_iam_role.runner[0].name policy_arn = element(var.runner_iam_role_managed_policy_arns, count.index) } - resource "aws_iam_role_policy" "ec2" { + count = var.iam_overrides["override_runner_role"] ? 0 : 1 name = "ec2" - role = aws_iam_role.runner.name + role = aws_iam_role.runner[0].name policy = templatefile("${path.module}/policies/instance-ec2.json", {}) } diff --git a/modules/runners/pool.tf b/modules/runners/pool.tf index 2762008ebf..8489b495dd 100644 --- a/modules/runners/pool.tf +++ b/modules/runners/pool.tf @@ -48,7 +48,7 @@ module "pool" { group_name = var.runner_group_name name_prefix = var.runner_name_prefix pool_owner = var.pool_runner_owner - role = aws_iam_role.runner + role = var.iam_overrides["override_runner_role"] ? var.iam_overrides["runner_role_arn"] : aws_iam_role.runner[0].name } subnet_ids = var.subnet_ids ssm_token_path = "${var.ssm_paths.root}/${var.ssm_paths.tokens}" diff --git a/modules/runners/scale-up.tf b/modules/runners/scale-up.tf index 9230267c07..01f5468728 100644 --- a/modules/runners/scale-up.tf +++ b/modules/runners/scale-up.tf @@ -112,7 +112,7 @@ resource "aws_iam_role_policy" "scale_up" { name = "scale-up-policy" role = aws_iam_role.scale_up.name policy = templatefile("${path.module}/policies/lambda-scale-up.json", { - arn_runner_instance_role = aws_iam_role.runner.arn + arn_runner_instance_role = var.iam_overrides["override_runner_role"] ? var.iam_overrides["runner_role_arn"] : aws_iam_role.runner[0].arn sqs_arn = var.sqs_build_queue.arn github_app_id_arn = var.github_app_parameters.id.arn github_app_key_base64_arn = var.github_app_parameters.key_base64.arn diff --git a/modules/runners/variables.tf b/modules/runners/variables.tf index a78231e7da..b1427fde2d 100644 --- a/modules/runners/variables.tf +++ b/modules/runners/variables.tf @@ -36,7 +36,7 @@ variable "subnet_ids" { } variable "overrides" { - description = "This map provides the possibility to override some defaults. The following attributes are supported: `name_sg` overrides the `Name` tag for all security groups created by this module. `name_runner_agent_instance` overrides the `Name` tag for the ec2 instance defined in the auto launch configuration. `name_docker_machine_runners` overrides the `Name` tag spot instances created by the runner agent." + description = "This map provides the possibility to override some defaults. The following attributes are supported: `name_sg` overrides the `Name` tag for all security groups created by this module. `name_runner` overrides the `Name` tag for the ec2 instance defined in the auto launch configuration. `instance_profile_name` overrides the instance profile name used in the launch template." type = map(string) default = { @@ -45,6 +45,23 @@ variable "overrides" { } } +variable "iam_overrides" { + description = "This map provides the possibility to override some IAM defaults. The following attributes are supported: `instance_profile_name` overrides the instance profile name used in the launch template. `runner_role_arn` overrides the IAM role ARN used for the runner instances." + type = object({ + override_instance_profile = optional(bool, null) + instance_profile_name = optional(string, null) + override_runner_role = optional(bool, null) + runner_role_arn = optional(string, null) + }) + + default = { + override_instance_profile = false + instance_profile_name = null + override_runner_role = false + runner_role_arn = null + } +} + variable "tags" { description = "Map of tags that will be added to created resources. By default resources will be tagged with name." type = map(string) diff --git a/variables.tf b/variables.tf index f412d2a486..dcafc8f849 100644 --- a/variables.tf +++ b/variables.tf @@ -108,6 +108,23 @@ variable "runner_group_name" { default = "Default" } +variable "iam_overrides" { + description = "This map provides the possibility to override some defaults. The following attributes are supported: `name_sg` overrides the `Name` tag for all security groups created by this module. `name_runner` overrides the `Name` tag for the ec2 instance defined in the auto launch configuration. `instance_profile_name` overrides the instance profile name used in the launch template." + type = object({ + override_instance_profile = optional(bool, null) + instance_profile_name = optional(string, null) + override_runner_role = optional(bool, null) + runner_role_arn = optional(string, null) + }) + + default = { + override_instance_profile = false + instance_profile_name = null + override_runner_role = false + runner_role_arn = null + } +} + variable "scale_up_reserved_concurrent_executions" { description = "Amount of reserved concurrent executions for the scale-up lambda function. A value of 0 disables lambda from being triggered and -1 removes any concurrency limitations." type = number From b09301d61ccb07443cf1d0407420c92bee29ad34 Mon Sep 17 00:00:00 2001 From: Marat Soltobaev Date: Thu, 6 Nov 2025 12:50:58 -0600 Subject: [PATCH 2/3] Update variable description --- variables.tf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/variables.tf b/variables.tf index dcafc8f849..8e10c91172 100644 --- a/variables.tf +++ b/variables.tf @@ -109,7 +109,7 @@ variable "runner_group_name" { } variable "iam_overrides" { - description = "This map provides the possibility to override some defaults. The following attributes are supported: `name_sg` overrides the `Name` tag for all security groups created by this module. `name_runner` overrides the `Name` tag for the ec2 instance defined in the auto launch configuration. `instance_profile_name` overrides the instance profile name used in the launch template." + description = "This map provides the possibility to override some IAM defaults. Note that when using this variable, you are responsible for ensuring the role has necessary permissions to access required resources; `override_instance_profile`: When set to true, the instance profile name provided in `instance_profile_name` will be used for the runners. `override_runner_role`: When set to true, the role ARN provided in `runner_role_arn` will be used for the runners." type = object({ override_instance_profile = optional(bool, null) instance_profile_name = optional(string, null) From 703763ffe224e8a204f4a561609788e511878e23 Mon Sep 17 00:00:00 2001 From: Marat Soltobaev Date: Thu, 6 Nov 2025 15:03:31 -0600 Subject: [PATCH 3/3] Add iam_overrides var to multi-runner module --- modules/multi-runner/variables.tf | 17 +++++++++++++++++ modules/runners/variables.tf | 2 +- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/modules/multi-runner/variables.tf b/modules/multi-runner/variables.tf index be35ad09f8..88e1951009 100644 --- a/modules/multi-runner/variables.tf +++ b/modules/multi-runner/variables.tf @@ -718,3 +718,20 @@ variable "user_agent" { type = string default = "github-aws-runners" } + +variable "iam_overrides" { + description = "This map provides the possibility to override some IAM defaults. The following attributes are supported: `instance_profile_name` overrides the instance profile name used in the launch template. `runner_role_arn` overrides the IAM role ARN used for the runner instances." + type = object({ + override_instance_profile = optional(bool, null) + instance_profile_name = optional(string, null) + override_runner_role = optional(bool, null) + runner_role_arn = optional(string, null) + }) + + default = { + override_instance_profile = false + instance_profile_name = null + override_runner_role = false + runner_role_arn = null + } +} diff --git a/modules/runners/variables.tf b/modules/runners/variables.tf index 79bc36f1fa..d324b749af 100644 --- a/modules/runners/variables.tf +++ b/modules/runners/variables.tf @@ -36,7 +36,7 @@ variable "subnet_ids" { } variable "overrides" { - description = "This map provides the possibility to override some defaults. The following attributes are supported: `name_sg` overrides the `Name` tag for all security groups created by this module. `name_runner` overrides the `Name` tag for the ec2 instance defined in the auto launch configuration. `instance_profile_name` overrides the instance profile name used in the launch template." + description = "This map provides the possibility to override some defaults. The following attributes are supported: `name_sg` overrides the `Name` tag for all security groups created by this module. `name_runner_agent_instance` overrides the `Name` tag for the ec2 instance defined in the auto launch configuration. `name_docker_machine_runners` overrides the `Name` tag spot instances created by the runner agent." type = map(string) default = {