Skip to content
Open
Show file tree
Hide file tree
Changes from 3 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
1 change: 1 addition & 0 deletions main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions modules/runners/logging.tf
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion modules/runners/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
41 changes: 23 additions & 18 deletions modules/runners/policies-runner.tf
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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
Copy link

Copilot AI Dec 6, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When override_runner_role is true, the aws_iam_role.runner resource is not created (count = 0). However, this resource still tries to reference aws_iam_role.runner[0].name when override_instance_profile is false, which will cause a Terraform error.

If override_runner_role is true, then override_instance_profile should also be true (or the instance profile creation should be skipped). Consider adding validation to enforce this constraint, or adjust the logic to handle this case properly.

Suggested change
count = var.iam_overrides["override_instance_profile"] ? 0 : 1
count = (var.iam_overrides["override_instance_profile"] || var.iam_overrides["override_runner_role"]) ? 0 : 1

Copilot uses AI. Check for mistakes.
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}"
Expand All @@ -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}"
Expand All @@ -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", {})
}

Expand Down
2 changes: 1 addition & 1 deletion modules/runners/pool.tf
Original file line number Diff line number Diff line change
Expand Up @@ -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
Copy link

Copilot AI Dec 6, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The role field is being set incorrectly. The pool module expects role to be an object with an arn field (see modules/runners/pool/variables.tf line 43-45), but this code is passing either a string ARN or a string name.

When override_runner_role is true, it passes var.iam_overrides["runner_role_arn"] which is a string ARN. When false, it passes aws_iam_role.runner[0].name which is a string name.

This should be:

role = var.iam_overrides["override_runner_role"] ? { arn = var.iam_overrides["runner_role_arn"] } : aws_iam_role.runner[0]

Or alternatively, use a local variable to construct the proper object structure.

Suggested change
role = var.iam_overrides["override_runner_role"] ? var.iam_overrides["runner_role_arn"] : aws_iam_role.runner[0].name
role = var.iam_overrides["override_runner_role"] ? { arn = var.iam_overrides["runner_role_arn"] } : aws_iam_role.runner[0]

Copilot uses AI. Check for mistakes.
}
subnet_ids = var.subnet_ids
ssm_token_path = "${var.ssm_paths.root}/${var.ssm_paths.tokens}"
Expand Down
2 changes: 1 addition & 1 deletion modules/runners/scale-up.tf
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,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
Expand Down
19 changes: 18 additions & 1 deletion modules/runners/variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {
Expand All @@ -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
}
Copy link

Copilot AI Dec 6, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The iam_overrides variable lacks validation to ensure that when override_instance_profile is true, the instance_profile_name is also provided (not null). Similarly, when override_runner_role is true, runner_role_arn should be provided.

Consider adding validation rules:

validation {
  condition     = !var.iam_overrides.override_instance_profile || var.iam_overrides.instance_profile_name != null
  error_message = "instance_profile_name must be provided when override_instance_profile is true."
}

validation {
  condition     = !var.iam_overrides.override_runner_role || var.iam_overrides.runner_role_arn != null
  error_message = "runner_role_arn must be provided when override_runner_role is true."
}
Suggested change
}
}
validation {
condition = !var.iam_overrides.override_instance_profile || var.iam_overrides.instance_profile_name != null
error_message = "instance_profile_name must be provided when override_instance_profile is true."
}
validation {
condition = !var.iam_overrides.override_runner_role || var.iam_overrides.runner_role_arn != null
error_message = "runner_role_arn must be provided when override_runner_role is true."
}

Copilot uses AI. Check for mistakes.
}

variable "tags" {
description = "Map of tags that will be added to created resources. By default resources will be tagged with name."
type = map(string)
Expand Down
17 changes: 17 additions & 0 deletions variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,23 @@ variable "runner_group_name" {
default = "Default"
}

variable "iam_overrides" {
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."
Copy link

Copilot AI Dec 6, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] The description mentions "When set to true, the instance profile name provided in instance_profile_name will be used" and "When set to true, the role ARN provided in runner_role_arn will be used", but this wording is slightly misleading.

It's clearer to phrase it as: "When set to true, uses the instance profile name specified in instance_profile_name instead of creating a new instance profile." This makes it explicit that when true, the module will NOT create the resource.

Suggested change
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."
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, uses the instance profile name specified in `instance_profile_name` instead of creating a new instance profile. `override_runner_role`: When set to true, uses the role ARN specified in `runner_role_arn` instead of creating a new IAM role."

Copilot uses AI. Check for mistakes.
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
}
Copy link

Copilot AI Dec 6, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The iam_overrides variable lacks validation to ensure that when override_instance_profile is true, the instance_profile_name is also provided (not null). Similarly, when override_runner_role is true, runner_role_arn should be provided.

Consider adding validation rules:

validation {
  condition     = !var.iam_overrides.override_instance_profile || var.iam_overrides.instance_profile_name != null
  error_message = "instance_profile_name must be provided when override_instance_profile is true."
}

validation {
  condition     = !var.iam_overrides.override_runner_role || var.iam_overrides.runner_role_arn != null
  error_message = "runner_role_arn must be provided when override_runner_role is true."
}
Suggested change
}
}
validation {
condition = !var.iam_overrides.override_instance_profile || var.iam_overrides.instance_profile_name != null
error_message = "instance_profile_name must be provided when override_instance_profile is true."
}
validation {
condition = !var.iam_overrides.override_runner_role || var.iam_overrides.runner_role_arn != null
error_message = "runner_role_arn must be provided when override_runner_role is true."
}

Copilot uses AI. Check for mistakes.
}

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
Expand Down
Loading