Skip to content

Commit 6642e57

Browse files
maratinvitaenpalmBrend-Smits
authored
feat(runner-role): Enable using separate IAM role for runners (github-aws-runners#4875)
### What Allow customization of runner IAM role ### Description This PR introduces the ability to explicitly specify an IAM role and instance profile for the runner instances. This is motivated by a need to accommodate legacy IAM roles that remain from previous infrastructure migrations. Proposed change is backward-compatible. --------- Co-authored-by: Niek Palm <npalm@users.noreply.github.com> Co-authored-by: Brend Smits <brend.smits@philips.com>
1 parent 3e179a3 commit 6642e57

10 files changed

Lines changed: 123 additions & 23 deletions

File tree

main.tf

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,7 @@ module "runners" {
159159
subnet_ids = var.subnet_ids
160160
prefix = var.prefix
161161
tags = local.tags
162+
iam_overrides = var.iam_overrides
162163

163164
ssm_paths = {
164165
root = local.ssm_root_path

modules/multi-runner/runners.tf

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ module "runners" {
106106
create_service_linked_role_spot = each.value.runner_config.create_service_linked_role_spot
107107

108108
runner_iam_role_managed_policy_arns = each.value.runner_config.runner_iam_role_managed_policy_arns
109+
iam_overrides = each.value.runner_config.iam_overrides
109110

110111
ghes_url = var.ghes_url
111112
ghes_ssl_verify = var.ghes_ssl_verify

modules/multi-runner/variables.tf

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,17 @@ variable "multi_runner_config" {
186186
lambda_timeout = optional(number, 30)
187187
max_attempts = optional(number, 1)
188188
}), {})
189+
iam_overrides = optional(object({
190+
override_instance_profile = optional(bool, null)
191+
instance_profile_name = optional(string, null)
192+
override_runner_role = optional(bool, null)
193+
runner_role_arn = optional(string, null)
194+
}), {
195+
override_instance_profile = false
196+
instance_profile_name = null
197+
override_runner_role = false
198+
runner_role_arn = null
199+
})
189200
})
190201
matcherConfig = object({
191202
labelMatchers = list(list(string))
@@ -259,6 +270,7 @@ variable "multi_runner_config" {
259270
block_device_mappings: "The EC2 instance block device configuration. Takes the following keys: `device_name`, `delete_on_termination`, `volume_type`, `volume_size`, `encrypted`, `iops`, `throughput`, `kms_key_id`, `snapshot_id`."
260271
job_retry: "Experimental! Can be removed / changed without trigger a major release. Configure job retries. The configuration enables job retries (for ephemeral runners). After creating the instances a message will be published to a job retry queue. The job retry check lambda is checking after a delay if the job is queued. If not the message will be published again on the scale-up (build queue). Using this feature can impact the rate limit of the GitHub app."
261272
pool_config: "The configuration for updating the pool. The `pool_size` to adjust to by the events triggered by the `schedule_expression`. For example you can configure a cron expression for week days to adjust the pool to 10 and another expression for the weekend to adjust the pool to 1. Use `schedule_expression_timezone` to override the schedule time zone (defaults to UTC)."
273+
iam_overrides: "Allows to (optionally) override the instance profile and runner role created by the module. Set `override_instance_profile` to true and provide the `instance_profile_name` to use an existing instance profile. Set `override_runner_role` to true and provide the `runner_role_arn` to use an existing role for the runner instances."
262274
}
263275
matcherConfig: {
264276
labelMatchers: "The list of list of labels supported by the runner configuration. `[[self-hosted, linux, x64, example]]`"
@@ -762,6 +774,33 @@ variable "user_agent" {
762774
default = "github-aws-runners"
763775
}
764776

777+
variable "iam_overrides" {
778+
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."
779+
type = object({
780+
override_instance_profile = optional(bool, null)
781+
instance_profile_name = optional(string, null)
782+
override_runner_role = optional(bool, null)
783+
runner_role_arn = optional(string, null)
784+
})
785+
786+
default = {
787+
override_instance_profile = false
788+
instance_profile_name = null
789+
override_runner_role = false
790+
runner_role_arn = null
791+
}
792+
793+
validation {
794+
condition = !var.iam_overrides.override_instance_profile || var.iam_overrides.instance_profile_name != null
795+
error_message = "instance_profile_name must be provided when override_instance_profile is true."
796+
}
797+
798+
validation {
799+
condition = !var.iam_overrides.override_runner_role || var.iam_overrides.runner_role_arn != null
800+
error_message = "runner_role_arn must be provided when override_runner_role is true."
801+
}
802+
}
803+
765804
variable "lambda_event_source_mapping_batch_size" {
766805
description = "Maximum number of records to pass to the lambda function in a single batch for the event source mapping. When not set, the AWS default of 10 events will be used."
767806
type = number

modules/runners/logging.tf

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,9 +74,9 @@ resource "aws_cloudwatch_log_group" "gh_runners" {
7474
}
7575

7676
resource "aws_iam_role_policy" "cloudwatch" {
77-
count = var.enable_cloudwatch_agent ? 1 : 0
77+
count = var.iam_overrides["override_runner_role"] ? 0 : (var.enable_cloudwatch_agent ? 1 : 0)
7878
name = "CloudWatchLogginAndMetrics"
79-
role = aws_iam_role.runner.name
79+
role = aws_iam_role.runner[0].name
8080
policy = templatefile("${path.module}/policies/instance-cloudwatch-policy.json",
8181
{
8282
ssm_parameter_arn = aws_ssm_parameter.cloudwatch_agent_config_runner[0].arn

modules/runners/main.tf

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -211,7 +211,7 @@ resource "aws_launch_template" "runner" {
211211
}
212212

213213
iam_instance_profile {
214-
name = aws_iam_instance_profile.runner.name
214+
name = var.iam_overrides["override_instance_profile"] ? var.iam_overrides["instance_profile_name"] : aws_iam_instance_profile.runner[0].name
215215
}
216216

217217
instance_initiated_shutdown_behavior = "terminate"

modules/runners/policies-runner.tf

Lines changed: 23 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
data "aws_caller_identity" "current" {}
22

33
resource "aws_iam_role" "runner" {
4+
count = var.iam_overrides["override_runner_role"] ? 0 : 1
45
name = "${substr("${var.prefix}-runner", 0, 54)}-${substr(md5("${var.prefix}-runner"), 0, 8)}"
56
assume_role_policy = templatefile("${path.module}/policies/instance-role-trust-policy.json", {})
67
path = local.role_path
@@ -9,22 +10,24 @@ resource "aws_iam_role" "runner" {
910
}
1011

1112
resource "aws_iam_instance_profile" "runner" {
12-
name = "${var.prefix}-runner-profile"
13-
role = aws_iam_role.runner.name
14-
path = local.instance_profile_path
15-
tags = local.tags
13+
count = (var.iam_overrides["override_instance_profile"] || var.iam_overrides["override_runner_role"]) ? 0 : 1
14+
name = "${var.prefix}-runner-profile"
15+
role = aws_iam_role.runner[0].name
16+
path = local.instance_profile_path
17+
tags = local.tags
1618
}
1719

1820
resource "aws_iam_role_policy" "runner_session_manager_aws_managed" {
21+
count = (var.iam_overrides["override_runner_role"] || var.iam_overrides["override_instance_profile"]) ? 0 : (var.enable_ssm_on_runners ? 1 : 0)
1922
name = "runner-ssm-session"
20-
count = var.enable_ssm_on_runners ? 1 : 0
21-
role = aws_iam_role.runner.name
23+
role = aws_iam_role.runner[0].name
2224
policy = templatefile("${path.module}/policies/instance-ssm-policy.json", {})
2325
}
2426

2527
resource "aws_iam_role_policy" "ssm_parameters" {
26-
name = "runner-ssm-parameters"
27-
role = aws_iam_role.runner.name
28+
count = (var.iam_overrides["override_runner_role"] || var.iam_overrides["override_instance_profile"]) ? 0 : 1
29+
name = "runner-ssm-parameters"
30+
role = aws_iam_role.runner[0].name
2831
policy = templatefile("${path.module}/policies/instance-ssm-parameters-policy.json",
2932
{
3033
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" {
3437
}
3538

3639
resource "aws_iam_role_policy" "dist_bucket" {
37-
count = var.enable_runner_binaries_syncer ? 1 : 0
40+
count = (var.iam_overrides["override_runner_role"] || var.iam_overrides["override_instance_profile"]) ? 0 : (var.enable_runner_binaries_syncer ? 1 : 0)
3841

3942
name = "distribution-bucket"
40-
role = aws_iam_role.runner.name
43+
role = aws_iam_role.runner[0].name
4144
policy = templatefile("${path.module}/policies/instance-s3-policy.json",
4245
{
4346
s3_arn = "${var.s3_runner_binaries.arn}/${var.s3_runner_binaries.key}"
@@ -46,33 +49,35 @@ resource "aws_iam_role_policy" "dist_bucket" {
4649
}
4750

4851
resource "aws_iam_role_policy_attachment" "xray_tracing" {
49-
count = var.tracing_config.mode != null ? 1 : 0
50-
role = aws_iam_role.runner.name
52+
count = (var.iam_overrides["override_runner_role"] || var.iam_overrides["override_instance_profile"]) ? 0 : (var.tracing_config.mode != null ? 1 : 0)
53+
role = aws_iam_role.runner[0].name
5154
policy_arn = "arn:${var.aws_partition}:iam::aws:policy/AWSXRayDaemonWriteAccess"
5255
}
5356

5457
resource "aws_iam_role_policy" "describe_tags" {
58+
count = (var.iam_overrides["override_runner_role"] || var.iam_overrides["override_instance_profile"]) ? 0 : 1
5559
name = "runner-describe-tags"
56-
role = aws_iam_role.runner.name
60+
role = aws_iam_role.runner[0].name
5761
policy = file("${path.module}/policies/instance-describe-tags-policy.json")
5862
}
5963

6064
resource "aws_iam_role_policy" "create_tag" {
65+
count = (var.iam_overrides["override_runner_role"] || var.iam_overrides["override_instance_profile"]) ? 0 : 1
6166
name = "runner-create-tags"
62-
role = aws_iam_role.runner.name
67+
role = aws_iam_role.runner[0].name
6368
policy = templatefile("${path.module}/policies/instance-create-tags-policy.json", {})
6469
}
6570

6671
resource "aws_iam_role_policy_attachment" "managed_policies" {
67-
count = length(var.runner_iam_role_managed_policy_arns)
68-
role = aws_iam_role.runner.name
72+
count = (var.iam_overrides["override_runner_role"] || var.iam_overrides["override_instance_profile"]) ? 0 : length(var.runner_iam_role_managed_policy_arns)
73+
role = aws_iam_role.runner[0].name
6974
policy_arn = element(var.runner_iam_role_managed_policy_arns, count.index)
7075
}
7176

72-
7377
resource "aws_iam_role_policy" "ec2" {
78+
count = (var.iam_overrides["override_runner_role"] || var.iam_overrides["override_instance_profile"]) ? 0 : 1
7479
name = "ec2"
75-
role = aws_iam_role.runner.name
80+
role = aws_iam_role.runner[0].name
7681
policy = templatefile("${path.module}/policies/instance-ec2.json", {})
7782
}
7883

modules/runners/pool.tf

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ module "pool" {
5151
group_name = var.runner_group_name
5252
name_prefix = var.runner_name_prefix
5353
pool_owner = var.pool_runner_owner
54-
role = aws_iam_role.runner
54+
role = var.iam_overrides["override_runner_role"] ? { arn = var.iam_overrides["runner_role_arn"] } : aws_iam_role.runner[0]
5555
use_dedicated_host = var.use_dedicated_host
5656
}
5757
subnet_ids = var.subnet_ids

modules/runners/scale-up.tf

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ resource "aws_iam_role_policy" "scale_up" {
120120
name = "scale-up-policy"
121121
role = aws_iam_role.scale_up.name
122122
policy = templatefile("${path.module}/policies/lambda-scale-up.json", {
123-
arn_runner_instance_role = aws_iam_role.runner.arn
123+
arn_runner_instance_role = var.iam_overrides["override_runner_role"] ? var.iam_overrides["runner_role_arn"] : aws_iam_role.runner[0].arn
124124
sqs_arn = var.sqs_build_queue.arn
125125
github_app_id_arn = var.github_app_parameters.id.arn
126126
github_app_key_base64_arn = var.github_app_parameters.key_base64.arn

modules/runners/variables.tf

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,33 @@ variable "overrides" {
4545
}
4646
}
4747

48+
variable "iam_overrides" {
49+
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."
50+
type = object({
51+
override_instance_profile = optional(bool, null)
52+
instance_profile_name = optional(string, null)
53+
override_runner_role = optional(bool, null)
54+
runner_role_arn = optional(string, null)
55+
})
56+
57+
default = {
58+
override_instance_profile = false
59+
instance_profile_name = null
60+
override_runner_role = false
61+
runner_role_arn = null
62+
}
63+
64+
validation {
65+
condition = !var.iam_overrides.override_instance_profile || var.iam_overrides.instance_profile_name != null
66+
error_message = "instance_profile_name must be provided when override_instance_profile is true."
67+
}
68+
69+
validation {
70+
condition = !var.iam_overrides.override_runner_role || var.iam_overrides.runner_role_arn != null
71+
error_message = "runner_role_arn must be provided when override_runner_role is true."
72+
}
73+
}
74+
4875
variable "tags" {
4976
description = "Map of tags that will be added to created resources. By default resources will be tagged with name."
5077
type = map(string)

variables.tf

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,33 @@ variable "runner_group_name" {
108108
default = "Default"
109109
}
110110

111+
variable "iam_overrides" {
112+
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."
113+
type = object({
114+
override_instance_profile = optional(bool, null)
115+
instance_profile_name = optional(string, null)
116+
override_runner_role = optional(bool, null)
117+
runner_role_arn = optional(string, null)
118+
})
119+
120+
default = {
121+
override_instance_profile = false
122+
instance_profile_name = null
123+
override_runner_role = false
124+
runner_role_arn = null
125+
}
126+
127+
validation {
128+
condition = !var.iam_overrides.override_instance_profile || var.iam_overrides.instance_profile_name != null
129+
error_message = "instance_profile_name must be provided when override_instance_profile is true."
130+
}
131+
132+
validation {
133+
condition = !var.iam_overrides.override_runner_role || var.iam_overrides.runner_role_arn != null
134+
error_message = "runner_role_arn must be provided when override_runner_role is true."
135+
}
136+
}
137+
111138
variable "scale_up_reserved_concurrent_executions" {
112139
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."
113140
type = number

0 commit comments

Comments
 (0)