diff --git a/README.md b/README.md
index 6e5994a305..ed4c622882 100644
--- a/README.md
+++ b/README.md
@@ -127,6 +127,7 @@ Join our discord community via [this invite link](https://discord.gg/bxgXW8jJGh)
| [disable\_runner\_autoupdate](#input\_disable\_runner\_autoupdate) | Disable the auto update of the github runner agent. Be aware there is a grace period of 30 days, see also the [GitHub article](https://github.blog/changelog/2022-02-01-github-actions-self-hosted-runners-can-now-disable-automatic-updates/) | `bool` | `false` | no |
| [enable\_ami\_housekeeper](#input\_enable\_ami\_housekeeper) | Option to disable the lambda to clean up old AMIs. | `bool` | `false` | no |
| [enable\_cloudwatch\_agent](#input\_enable\_cloudwatch\_agent) | Enables the cloudwatch agent on the ec2 runner instances. The runner uses a default config that can be overridden via `cloudwatch_config`. | `bool` | `true` | no |
+| [enable\_enterprise\_runners](#input\_enable\_enterprise\_runners) | Enable enterprise-level runners; when true, authentication must use enterprise\_pat. | `bool` | `false` | no |
| [enable\_ephemeral\_runners](#input\_enable\_ephemeral\_runners) | Enable ephemeral runners, runners will only be used once. | `bool` | `false` | no |
| [enable\_jit\_config](#input\_enable\_jit\_config) | Overwrite the default behavior for JIT configuration. By default JIT configuration is enabled for ephemeral runners and disabled for non-ephemeral runners. In case of GHES check first if the JIT config API is avaialbe. In case you upgradeing from 3.x to 4.x you can set `enable_jit_config` to `false` to avoid a breaking change when having your own AMI. | `bool` | `null` | no |
| [enable\_job\_queued\_check](#input\_enable\_job\_queued\_check) | Only scale if the job event received by the scale up lambda is in the queued state. By default enabled for non ephemeral runners and disabled for ephemeral. Set this variable to overwrite the default behavior. | `bool` | `null` | no |
@@ -139,10 +140,12 @@ Join our discord community via [this invite link](https://discord.gg/bxgXW8jJGh)
| [enable\_ssm\_on\_runners](#input\_enable\_ssm\_on\_runners) | Enable to allow access to the runner instances for debugging purposes via SSM. Note that this adds additional permissions to the runner instances. | `bool` | `false` | no |
| [enable\_user\_data\_debug\_logging\_runner](#input\_enable\_user\_data\_debug\_logging\_runner) | Option to enable debug logging for user-data, this logs all secrets as well. | `bool` | `false` | no |
| [enable\_userdata](#input\_enable\_userdata) | Should the userdata script be enabled for the runner. Set this to false if you are using your own prebuilt AMI. | `bool` | `true` | no |
+| [enterprise\_pat](#input\_enterprise\_pat) | GitHub enterprise PAT. Used only when enable\_enterprise\_runners is true. | `string` | `null` | no |
+| [enterprise\_slug](#input\_enterprise\_slug) | Enterprise slug | `string` | `""` | no |
| [eventbridge](#input\_eventbridge) | Enable the use of EventBridge by the module. By enabling this feature events will be put on the EventBridge by the webhook instead of directly dispatching to queues for scaling.
`enable`: Enable the EventBridge feature.
`accept_events`: List can be used to only allow specific events to be putted on the EventBridge. By default all events, empty list will be be interpreted as all events. |
object({
enable = optional(bool, true)
accept_events = optional(list(string), null)
}) | `{}` | no |
| [ghes\_ssl\_verify](#input\_ghes\_ssl\_verify) | GitHub Enterprise SSL verification. Set to 'false' when custom certificate (chains) is used for GitHub Enterprise Server (insecure). | `bool` | `true` | no |
| [ghes\_url](#input\_ghes\_url) | GitHub Enterprise Server URL. Example: https://github.internal.co - DO NOT SET IF USING PUBLIC GITHUB. However if you are using Github Enterprise Cloud with data-residency (ghe.com), set the endpoint here. Example - https://companyname.ghe.com | `string` | `null` | no |
-| [github\_app](#input\_github\_app) | GitHub app parameters, see your github app.object({
key_base64 = optional(string)
key_base64_ssm = optional(object({
arn = string
name = string
}))
id = optional(string)
id_ssm = optional(object({
arn = string
name = string
}))
webhook_secret = optional(string)
webhook_secret_ssm = optional(object({
arn = string
name = string
}))
}) | n/a | yes |
+| [github\_app](#input\_github\_app) | GitHub app parameters. | object({
key_base64 = optional(string, null)
key_base64_ssm = optional(object({ arn = string, name = string }))
id = optional(string, null)
id_ssm = optional(object({ arn = string, name = string }))
webhook_secret = optional(string)
webhook_secret_ssm = optional(object({ arn = string, name = string }))
}) | n/a | yes |
| [idle\_config](#input\_idle\_config) | List of time periods, defined as a cron expression, to keep a minimum amount of runners active instead of scaling down to 0. By defining this list you can ensure that in time periods that match the cron expression within 5 seconds a runner is kept idle. | list(object({
cron = string
timeZone = string
idleCount = number
evictionStrategy = optional(string, "oldest_first")
})) | `[]` | no |
| [instance\_allocation\_strategy](#input\_instance\_allocation\_strategy) | The allocation strategy for spot instances. AWS recommends using `price-capacity-optimized` however the AWS default is `lowest-price`. | `string` | `"lowest-price"` | no |
| [instance\_max\_spot\_price](#input\_instance\_max\_spot\_price) | Max price price for spot instances per hour. This variable will be passed to the create fleet as max spot price for the fleet. | `string` | `null` | no |
diff --git a/lambdas/functions/control-plane/src/aws/runners.d.ts b/lambdas/functions/control-plane/src/aws/runners.d.ts
index 72ff9e3e1a..074cc0ee25 100644
--- a/lambdas/functions/control-plane/src/aws/runners.d.ts
+++ b/lambdas/functions/control-plane/src/aws/runners.d.ts
@@ -1,6 +1,6 @@
import { DefaultTargetCapacityType, SpotAllocationStrategy } from '@aws-sdk/client-ec2';
-export type RunnerType = 'Org' | 'Repo';
+export type RunnerType = 'Enterprise' | 'Org' | 'Repo';
export interface RunnerList {
instanceId: string;
diff --git a/lambdas/functions/control-plane/src/aws/runners.test.ts b/lambdas/functions/control-plane/src/aws/runners.test.ts
index c4ec328c9b..b40280b3db 100644
--- a/lambdas/functions/control-plane/src/aws/runners.test.ts
+++ b/lambdas/functions/control-plane/src/aws/runners.test.ts
@@ -661,7 +661,7 @@ function createRunnerConfig(runnerConfig: RunnerConfig): RunnerInputParameters {
}
interface ExpectedFleetRequestValues {
- type: 'Repo' | 'Org';
+ type: 'Enterprise' | 'Repo' | 'Org';
capacityType: DefaultTargetCapacityType;
allocationStrategy: SpotAllocationStrategy;
maxSpotPrice?: string;
diff --git a/lambdas/functions/control-plane/src/github/octokit.test.ts b/lambdas/functions/control-plane/src/github/octokit.test.ts
index a715a15476..2dcfed5104 100644
--- a/lambdas/functions/control-plane/src/github/octokit.test.ts
+++ b/lambdas/functions/control-plane/src/github/octokit.test.ts
@@ -10,6 +10,10 @@ const mockOctokit = {
},
};
+function setDefaults() {
+ process.env.PARAMETER_ENTERPRISE_PAT_NAME = 'github-pat-id';
+}
+
vi.mock('../github/auth', async () => ({
createGithubInstallationAuth: vi.fn().mockImplementation(async (installationId) => {
return { token: 'token', type: 'installation', installationId: installationId };
@@ -22,29 +26,46 @@ vi.mock('@octokit/rest', async () => ({
Octokit: vi.fn().mockImplementation(() => mockOctokit),
}));
+vi.mock('@aws-github-runner/aws-ssm-util', async () => {
+ const actual = (await vi.importActual(
+ '@aws-github-runner/aws-ssm-util',
+ )) as typeof import('@aws-github-runner/aws-ssm-util');
+
+ return {
+ ...actual,
+ getParameter: vi.fn(),
+ };
+});
+
// We've already mocked '../github/auth' above
describe('Test getOctokit', () => {
const data = [
{
description: 'Should look-up org installation if installationId is 0.',
- input: { orgLevelRunner: false, installationId: 0 },
- output: { callReposInstallation: true, callOrgInstallation: false },
+ input: { enableEnterpriseLevel: false, orgLevelRunner: false, installationId: 0 },
+ output: { callEnterpriseToken: false, callReposInstallation: true, callOrgInstallation: false },
},
{
description: 'Should look-up org installation if installationId is 0.',
- input: { orgLevelRunner: true, installationId: 0 },
- output: { callReposInstallation: false, callOrgInstallation: true },
+ input: { enableEnterpriseLevel: false, orgLevelRunner: true, installationId: 0 },
+ output: { callEnterpriseToken: false, callReposInstallation: false, callOrgInstallation: true },
},
{
description: 'Should not look-up org installation if provided in payload.',
- input: { orgLevelRunner: true, installationId: 1 },
- output: { callReposInstallation: false, callOrgInstallation: false },
+ input: { enableEnterpriseLevel: false, orgLevelRunner: true, installationId: 1 },
+ output: { callEnterpriseToken: false, callReposInstallation: false, callOrgInstallation: false },
+ },
+ {
+ description: 'Should not look-up org installation if enterprise is enabled.',
+ input: { enableEnterpriseLevel: true, orgLevelRunner: false, installationId: 1 },
+ output: { callEnterpriseToken: true, callReposInstallation: false, callOrgInstallation: false },
},
];
beforeEach(() => {
vi.clearAllMocks();
+ setDefaults();
});
it.each(data)(`$description`, async ({ input, output }) => {
@@ -64,9 +85,12 @@ describe('Test getOctokit', () => {
mockOctokit.apps.getOrgInstallation.mockRejectedValue(new Error('Error'));
}
- await expect(getOctokit('', input.orgLevelRunner, payload)).resolves.toBeDefined();
+ await expect(getOctokit('', input.enableEnterpriseLevel, input.orgLevelRunner, payload)).resolves.toBeDefined();
- if (output.callOrgInstallation) {
+ if (output.callEnterpriseToken) {
+ expect(mockOctokit.apps.getOrgInstallation).not.toHaveBeenCalled();
+ expect(mockOctokit.apps.getRepoInstallation).not.toHaveBeenCalled();
+ } else if (output.callOrgInstallation) {
expect(mockOctokit.apps.getOrgInstallation).toHaveBeenCalled();
expect(mockOctokit.apps.getRepoInstallation).not.toHaveBeenCalled();
} else if (output.callReposInstallation) {
diff --git a/lambdas/functions/control-plane/src/github/octokit.ts b/lambdas/functions/control-plane/src/github/octokit.ts
index a2cce5f55d..32b87ca8dc 100644
--- a/lambdas/functions/control-plane/src/github/octokit.ts
+++ b/lambdas/functions/control-plane/src/github/octokit.ts
@@ -1,6 +1,7 @@
import { Octokit } from '@octokit/rest';
import { ActionRequestMessage } from '../scale-runners/scale-up';
import { createGithubAppAuth, createGithubInstallationAuth, createOctokitClient } from './auth';
+import { getParameter } from '@aws-github-runner/aws-ssm-util';
export async function getInstallationId(
ghesApiUrl: string,
@@ -37,10 +38,17 @@ export async function getInstallationId(
*/
export async function getOctokit(
ghesApiUrl: string,
+ enableEnterpriseLevel: boolean,
enableOrgLevel: boolean,
payload: ActionRequestMessage,
): Promiseobject({
enable = optional(bool, true)
accept_events = optional(list(string), [])
}) | `{}` | no |
| [ghes\_ssl\_verify](#input\_ghes\_ssl\_verify) | GitHub Enterprise SSL verification. Set to 'false' when custom certificate (chains) is used for GitHub Enterprise Server (insecure). | `bool` | `true` | no |
| [ghes\_url](#input\_ghes\_url) | GitHub Enterprise Server URL. Example: https://github.internal.co - DO NOT SET IF USING PUBLIC GITHUB. .However if you are using Github Enterprise Cloud with data-residency (ghe.com), set the endpoint here. Example - https://companyname.ghe.com\| | `string` | `null` | no |
-| [github\_app](#input\_github\_app) | GitHub app parameters, see your github app.object({
key_base64 = optional(string)
key_base64_ssm = optional(object({
arn = string
name = string
}))
id = optional(string)
id_ssm = optional(object({
arn = string
name = string
}))
webhook_secret = optional(string)
webhook_secret_ssm = optional(object({
arn = string
name = string
}))
}) | n/a | yes |
+| [github\_app](#input\_github\_app) | GitHub app parameters. | object({
key_base64 = optional(string, null)
key_base64_ssm = optional(object({ arn = string, name = string }))
id = optional(string, null)
id_ssm = optional(object({ arn = string, name = string }))
webhook_secret = optional(string)
webhook_secret_ssm = optional(object({ arn = string, name = string }))
}) | n/a | yes |
| [instance\_profile\_path](#input\_instance\_profile\_path) | The path that will be added to the instance\_profile, if not set the environment name will be used. | `string` | `null` | no |
| [instance\_termination\_watcher](#input\_instance\_termination\_watcher) | Configuration for the spot termination watcher lambda function. This feature is Beta, changes will not trigger a major release as long in beta.object({
enable = optional(bool, false)
features = optional(object({
enable_spot_termination_handler = optional(bool, true)
enable_spot_termination_notification_watcher = optional(bool, true)
}), {})
memory_size = optional(number, null)
s3_key = optional(string, null)
s3_object_version = optional(string, null)
timeout = optional(number, null)
zip = optional(string, null)
}) | `{}` | no |
| [key\_name](#input\_key\_name) | Key pair name | `string` | `null` | no |
@@ -148,7 +149,7 @@ module "multi-runner" {
| [logging\_retention\_in\_days](#input\_logging\_retention\_in\_days) | Specifies the number of days you want to retain log events for the lambda log group. Possible values are: 0, 1, 3, 5, 7, 14, 30, 60, 90, 120, 150, 180, 365, 400, 545, 731, 1827, and 3653. | `number` | `180` | no |
| [matcher\_config\_parameter\_store\_tier](#input\_matcher\_config\_parameter\_store\_tier) | The tier of the parameter store for the matcher configuration. Valid values are `Standard`, and `Advanced`. | `string` | `"Standard"` | no |
| [metrics](#input\_metrics) | Configuration for metrics created by the module, by default metrics are disabled to avoid additional costs. When metrics are enable all metrics are created unless explicit configured otherwise. | object({
enable = optional(bool, false)
namespace = optional(string, "GitHub Runners")
metric = optional(object({
enable_github_app_rate_limit = optional(bool, true)
enable_job_retry = optional(bool, true)
enable_spot_termination_warning = optional(bool, true)
}), {})
}) | `{}` | no |
-| [multi\_runner\_config](#input\_multi\_runner\_config) | multi\_runner\_config = {map(object({
runner_config = object({
runner_os = string
runner_architecture = string
runner_metadata_options = optional(map(any), {
instance_metadata_tags = "enabled"
http_endpoint = "enabled"
http_tokens = "required"
http_put_response_hop_limit = 1
})
ami = optional(object({
filter = optional(map(list(string)), { state = ["available"] })
owners = optional(list(string), ["amazon"])
id_ssm_parameter_arn = optional(string, null)
kms_key_arn = optional(string, null)
}), null) # Defaults to null, in which case the module falls back to individual AMI variables (deprecated)
# Deprecated: Use ami object instead
ami_filter = optional(map(list(string)), { state = ["available"] })
ami_owners = optional(list(string), ["amazon"])
ami_id_ssm_parameter_name = optional(string, null)
ami_kms_key_arn = optional(string, "")
create_service_linked_role_spot = optional(bool, false)
credit_specification = optional(string, null)
delay_webhook_event = optional(number, 30)
disable_runner_autoupdate = optional(bool, false)
ebs_optimized = optional(bool, false)
enable_ephemeral_runners = optional(bool, false)
enable_job_queued_check = optional(bool, null)
enable_on_demand_failover_for_errors = optional(list(string), [])
enable_organization_runners = optional(bool, false)
enable_runner_binaries_syncer = optional(bool, true)
enable_ssm_on_runners = optional(bool, false)
enable_userdata = optional(bool, true)
instance_allocation_strategy = optional(string, "lowest-price")
instance_max_spot_price = optional(string, null)
instance_target_capacity_type = optional(string, "spot")
instance_types = list(string)
job_queue_retention_in_seconds = optional(number, 86400)
minimum_running_time_in_minutes = optional(number, null)
pool_runner_owner = optional(string, null)
runner_as_root = optional(bool, false)
runner_boot_time_in_minutes = optional(number, 5)
runner_disable_default_labels = optional(bool, false)
runner_extra_labels = optional(list(string), [])
runner_group_name = optional(string, "Default")
runner_name_prefix = optional(string, "")
runner_run_as = optional(string, "ec2-user")
runners_maximum_count = number
runner_additional_security_group_ids = optional(list(string), [])
scale_down_schedule_expression = optional(string, "cron(*/5 * * * ? *)")
scale_up_reserved_concurrent_executions = optional(number, 1)
userdata_template = optional(string, null)
userdata_content = optional(string, null)
enable_jit_config = optional(bool, null)
enable_runner_detailed_monitoring = optional(bool, false)
enable_cloudwatch_agent = optional(bool, true)
cloudwatch_config = optional(string, null)
userdata_pre_install = optional(string, "")
userdata_post_install = optional(string, "")
runner_hook_job_started = optional(string, "")
runner_hook_job_completed = optional(string, "")
runner_ec2_tags = optional(map(string), {})
runner_iam_role_managed_policy_arns = optional(list(string), [])
vpc_id = optional(string, null)
subnet_ids = optional(list(string), null)
idle_config = optional(list(object({
cron = string
timeZone = string
idleCount = number
evictionStrategy = optional(string, "oldest_first")
})), [])
cpu_options = optional(object({
core_count = number
threads_per_core = number
}), null)
runner_log_files = optional(list(object({
log_group_name = string
prefix_log_group = bool
file_path = string
log_stream_name = 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_size = number
volume_type = optional(string, "gp3")
})), [{
volume_size = 30
}])
pool_config = optional(list(object({
schedule_expression = string
schedule_expression_timezone = optional(string)
size = number
})), [])
job_retry = optional(object({
enable = optional(bool, false)
delay_in_seconds = optional(number, 300)
delay_backoff = optional(number, 2)
lambda_memory_size = optional(number, 256)
lambda_timeout = optional(number, 30)
max_attempts = optional(number, 1)
}), {})
})
matcherConfig = object({
labelMatchers = list(list(string))
exactMatch = optional(bool, false)
priority = optional(number, 999)
})
redrive_build_queue = optional(object({
enabled = bool
maxReceiveCount = number
}), {
enabled = false
maxReceiveCount = null
})
})) | n/a | yes |
+| [multi\_runner\_config](#input\_multi\_runner\_config) | multi\_runner\_config = {map(object({
runner_config = object({
runner_os = string
runner_architecture = string
runner_metadata_options = optional(map(any), {
instance_metadata_tags = "enabled"
http_endpoint = "enabled"
http_tokens = "required"
http_put_response_hop_limit = 1
})
ami = optional(object({
filter = optional(map(list(string)), { state = ["available"] })
owners = optional(list(string), ["amazon"])
id_ssm_parameter_arn = optional(string, null)
kms_key_arn = optional(string, null)
}), null) # Defaults to null, in which case the module falls back to individual AMI variables (deprecated)
# Deprecated: Use ami object instead
ami_filter = optional(map(list(string)), { state = ["available"] })
ami_owners = optional(list(string), ["amazon"])
ami_id_ssm_parameter_name = optional(string, null)
ami_kms_key_arn = optional(string, "")
create_service_linked_role_spot = optional(bool, false)
credit_specification = optional(string, null)
delay_webhook_event = optional(number, 30)
disable_runner_autoupdate = optional(bool, false)
ebs_optimized = optional(bool, false)
enable_ephemeral_runners = optional(bool, false)
enable_job_queued_check = optional(bool, null)
enable_on_demand_failover_for_errors = optional(list(string), [])
enable_organization_runners = optional(bool, false)
enable_enterprise_runners = optional(bool, false)
enterprise_slug = optional(string, "")
enterprise_pat = optional(string, "")
enable_runner_binaries_syncer = optional(bool, true)
enable_ssm_on_runners = optional(bool, false)
enable_userdata = optional(bool, true)
instance_allocation_strategy = optional(string, "lowest-price")
instance_max_spot_price = optional(string, null)
instance_target_capacity_type = optional(string, "spot")
instance_types = list(string)
job_queue_retention_in_seconds = optional(number, 86400)
minimum_running_time_in_minutes = optional(number, null)
pool_runner_owner = optional(string, null)
runner_as_root = optional(bool, false)
runner_boot_time_in_minutes = optional(number, 5)
runner_disable_default_labels = optional(bool, false)
runner_extra_labels = optional(list(string), [])
runner_group_name = optional(string, "Default")
runner_name_prefix = optional(string, "")
runner_run_as = optional(string, "ec2-user")
runners_maximum_count = number
runner_additional_security_group_ids = optional(list(string), [])
scale_down_schedule_expression = optional(string, "cron(*/5 * * * ? *)")
scale_up_reserved_concurrent_executions = optional(number, 1)
userdata_template = optional(string, null)
userdata_content = optional(string, null)
enable_jit_config = optional(bool, null)
enable_runner_detailed_monitoring = optional(bool, false)
enable_cloudwatch_agent = optional(bool, true)
cloudwatch_config = optional(string, null)
userdata_pre_install = optional(string, "")
userdata_post_install = optional(string, "")
runner_hook_job_started = optional(string, "")
runner_hook_job_completed = optional(string, "")
runner_ec2_tags = optional(map(string), {})
runner_iam_role_managed_policy_arns = optional(list(string), [])
vpc_id = optional(string, null)
subnet_ids = optional(list(string), null)
idle_config = optional(list(object({
cron = string
timeZone = string
idleCount = number
evictionStrategy = optional(string, "oldest_first")
})), [])
cpu_options = optional(object({
core_count = number
threads_per_core = number
}), null)
runner_log_files = optional(list(object({
log_group_name = string
prefix_log_group = bool
file_path = string
log_stream_name = 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_size = number
volume_type = optional(string, "gp3")
})), [{
volume_size = 30
}])
pool_config = optional(list(object({
schedule_expression = string
schedule_expression_timezone = optional(string)
size = number
})), [])
job_retry = optional(object({
enable = optional(bool, false)
delay_in_seconds = optional(number, 300)
delay_backoff = optional(number, 2)
lambda_memory_size = optional(number, 256)
lambda_timeout = optional(number, 30)
max_attempts = optional(number, 1)
}), {})
})
matcherConfig = object({
labelMatchers = list(list(string))
exactMatch = optional(bool, false)
priority = optional(number, 999)
})
redrive_build_queue = optional(object({
enabled = bool
maxReceiveCount = number
}), {
enabled = false
maxReceiveCount = null
})
})) | n/a | yes |
| [pool\_lambda\_reserved\_concurrent\_executions](#input\_pool\_lambda\_reserved\_concurrent\_executions) | 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. | `number` | `1` | no |
| [pool\_lambda\_timeout](#input\_pool\_lambda\_timeout) | Time out for the pool lambda in seconds. | `number` | `60` | no |
| [prefix](#input\_prefix) | The prefix used for naming resources | `string` | `"github-actions"` | no |
diff --git a/modules/multi-runner/main.tf b/modules/multi-runner/main.tf
index 905cc7f793..6543d49e2f 100644
--- a/modules/multi-runner/main.tf
+++ b/modules/multi-runner/main.tf
@@ -4,11 +4,13 @@ locals {
})
github_app_parameters = {
- id = coalesce(var.github_app.id_ssm, module.ssm.parameters.github_app_id)
- key_base64 = coalesce(var.github_app.key_base64_ssm, module.ssm.parameters.github_app_key_base64)
+ id = var.enterprise_pat == null ? coalesce(var.github_app.id_ssm, module.ssm.parameters.github_app_id) : null
+ key_base64 = var.enterprise_pat == null ? coalesce(var.github_app.key_base64_ssm, module.ssm.parameters.github_app_key_base64) : null
webhook_secret = coalesce(var.github_app.webhook_secret_ssm, module.ssm.parameters.github_app_webhook_secret)
}
+ enterprise_pat = var.enterprise_pat != null ? module.ssm.parameters.enterprise_pat : null
+
runner_extra_labels = { for k, v in var.multi_runner_config : k => sort(setunion(flatten(v.matcherConfig.labelMatchers), compact(v.runner_config.runner_extra_labels))) }
runner_config = { for k, v in var.multi_runner_config : k => merge({ id = aws_sqs_queue.queued_builds[k].id, arn = aws_sqs_queue.queued_builds[k].arn, url = aws_sqs_queue.queued_builds[k].url }, merge(v, { runner_config = merge(v.runner_config, { runner_extra_labels = local.runner_extra_labels[k] }) })) }
diff --git a/modules/multi-runner/outputs.tf b/modules/multi-runner/outputs.tf
index 2f2b1d3458..795a1f4a96 100644
--- a/modules/multi-runner/outputs.tf
+++ b/modules/multi-runner/outputs.tf
@@ -45,10 +45,13 @@ output "webhook" {
}
output "ssm_parameters" {
- value = { for k, v in local.github_app_parameters : k => {
- name = v.name
- arn = v.arn
+ value = {
+ for k, v in local.github_app_parameters :
+ k => {
+ name = v.name
+ arn = v.arn
}
+ if v != null
}
}
diff --git a/modules/multi-runner/runners.tf b/modules/multi-runner/runners.tf
index 811ab36260..323bb26cd7 100644
--- a/modules/multi-runner/runners.tf
+++ b/modules/multi-runner/runners.tf
@@ -33,9 +33,12 @@ module "runners" {
sqs_build_queue = { "arn" : each.value.arn, "url" : each.value.url }
github_app_parameters = local.github_app_parameters
+ enterprise_pat = local.enterprise_pat
ebs_optimized = each.value.runner_config.ebs_optimized
enable_on_demand_failover_for_errors = each.value.runner_config.enable_on_demand_failover_for_errors
enable_organization_runners = each.value.runner_config.enable_organization_runners
+ enable_enterprise_runners = each.value.runner_config.enable_enterprise_runners
+ enterprise_slug = each.value.runner_config.enterprise_slug
enable_ephemeral_runners = each.value.runner_config.enable_ephemeral_runners
enable_jit_config = each.value.runner_config.enable_jit_config
enable_job_queued_check = each.value.runner_config.enable_job_queued_check
diff --git a/modules/multi-runner/ssm.tf b/modules/multi-runner/ssm.tf
index 6a3a234e6f..e59fe65a77 100644
--- a/modules/multi-runner/ssm.tf
+++ b/modules/multi-runner/ssm.tf
@@ -1,7 +1,8 @@
module "ssm" {
- source = "../ssm"
- kms_key_arn = var.kms_key_arn
- path_prefix = "${local.ssm_root_path}/${var.ssm_paths.app}"
- github_app = var.github_app
- tags = local.tags
+ source = "../ssm"
+ kms_key_arn = var.kms_key_arn
+ path_prefix = "${local.ssm_root_path}/${var.ssm_paths.app}"
+ enterprise_pat = var.enterprise_pat
+ github_app = var.github_app
+ tags = local.tags
}
diff --git a/modules/multi-runner/variables.tf b/modules/multi-runner/variables.tf
index edbdb33059..d28af7c8da 100644
--- a/modules/multi-runner/variables.tf
+++ b/modules/multi-runner/variables.tf
@@ -1,37 +1,48 @@
+variable "enterprise_pat" {
+ description = "GitHub enterprise PAT. Used when not authenticating via GitHub App."
+ type = string
+ default = null
+}
+
variable "github_app" {
description = <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
})) | [| no | | [enable\_cloudwatch\_agent](#input\_enable\_cloudwatch\_agent) | Enabling the cloudwatch agent on the ec2 runner instances, the runner contains default config. Configuration can be overridden via `cloudwatch_config`. | `bool` | `true` | no | +| [enable\_enterprise\_runners](#input\_enable\_enterprise\_runners) | Register runners to organization, instead of repo or organization level | `bool` | n/a | yes | | [enable\_ephemeral\_runners](#input\_enable\_ephemeral\_runners) | Enable ephemeral runners, runners will only be used once. | `bool` | `false` | no | | [enable\_jit\_config](#input\_enable\_jit\_config) | Overwrite the default behavior for JIT configuration. By default JIT configuration is enabled for ephemeral runners and disabled for non-ephemeral runners. In case of GHES check first if the JIT config API is avaialbe. In case you upgradeing from 3.x to 4.x you can set `enable_jit_config` to `false` to avoid a breaking change when having your own AMI. | `bool` | `null` | no | | [enable\_job\_queued\_check](#input\_enable\_job\_queued\_check) | Only scale if the job event received by the scale up lambda is is in the state queued. By default enabled for non ephemeral runners and disabled for ephemeral. Set this variable to overwrite the default behavior. | `bool` | `null` | no | @@ -164,6 +165,8 @@ yarn run dist | [enable\_ssm\_on\_runners](#input\_enable\_ssm\_on\_runners) | Enable to allow access to the runner instances for debugging purposes via SSM. Note that this adds additional permissions to the runner instances. | `bool` | n/a | yes | | [enable\_user\_data\_debug\_logging](#input\_enable\_user\_data\_debug\_logging) | Option to enable debug logging for user-data, this logs all secrets as well. | `bool` | `false` | no | | [enable\_userdata](#input\_enable\_userdata) | Should the userdata script be enabled for the runner. Set this to false if you are using your own prebuilt AMI | `bool` | `true` | no | +| [enterprise\_pat](#input\_enterprise\_pat) | GitHub Enterprise PAT to use for registering runners to the enterprise. This is only required when `enable_enterprise_runners` is set to true. | `map(string)` | n/a | yes | +| [enterprise\_slug](#input\_enterprise\_slug) | Enterprise slug | `string` | n/a | yes | | [ghes\_ssl\_verify](#input\_ghes\_ssl\_verify) | GitHub Enterprise SSL verification. Set to 'false' when custom certificate (chains) is used for GitHub Enterprise Server (insecure). | `bool` | `true` | no | | [ghes\_url](#input\_ghes\_url) | GitHub Enterprise Server URL. DO NOT SET IF USING PUBLIC GITHUB..However if you are using Github Enterprise Cloud with data-residency (ghe.com), set the endpoint here. Example - https://companyname.ghe.com\| | `string` | `null` | no | | [github\_app\_parameters](#input\_github\_app\_parameters) | Parameter Store for GitHub App Parameters. |
{
"cidr_blocks": [
"0.0.0.0/0"
],
"description": null,
"from_port": 0,
"ipv6_cidr_blocks": [
"::/0"
],
"prefix_list_ids": null,
"protocol": "-1",
"security_groups": null,
"self": null,
"to_port": 0
}
]
object({
key_base64 = map(string)
id = map(string)
}) | n/a | yes |
diff --git a/modules/runners/job-retry.tf b/modules/runners/job-retry.tf
index e51c3903d4..3f7971f628 100644
--- a/modules/runners/job-retry.tf
+++ b/modules/runners/job-retry.tf
@@ -24,7 +24,10 @@ locals {
zip = var.lambda_zip
tracing_config = var.tracing_config
github_app_parameters = var.github_app_parameters
+ enterprise_pat = var.enterprise_pat
enable_organization_runners = var.enable_organization_runners
+ enable_enterprise_runners = var.enable_enterprise_runners
+ enterprise_slug = var.enterprise_slug
sqs_build_queue = var.sqs_build_queue
ghes_url = var.ghes_url
}
diff --git a/modules/runners/job-retry/README.md b/modules/runners/job-retry/README.md
index 91089a213b..50cfaf4a47 100644
--- a/modules/runners/job-retry/README.md
+++ b/modules/runners/job-retry/README.md
@@ -42,7 +42,7 @@ The module is an inner module and used by the runner module when the opt-in feat
| Name | Description | Type | Default | Required |
|------|-------------|------|---------|:--------:|
-| [config](#input\_config) | Configuration for the spot termination watcher lambda function.object({
aws_partition = optional(string, null)
architecture = optional(string, null)
enable_organization_runners = bool
environment_variables = optional(map(string), {})
ghes_url = optional(string, null)
user_agent = optional(string, null)
github_app_parameters = object({
key_base64 = map(string)
id = map(string)
})
kms_key_arn = optional(string, null)
lambda_tags = optional(map(string), {})
log_level = optional(string, null)
logging_kms_key_id = optional(string, null)
logging_retention_in_days = optional(number, null)
memory_size = optional(number, null)
metrics = optional(object({
enable = optional(bool, false)
namespace = optional(string, null)
metric = optional(object({
enable_github_app_rate_limit = optional(bool, true)
enable_job_retry = optional(bool, true)
}), {})
}), {})
prefix = optional(string, null)
principals = optional(list(object({
type = string
identifiers = list(string)
})), [])
queue_encryption = optional(object({
kms_data_key_reuse_period_seconds = optional(number, null)
kms_master_key_id = optional(string, null)
sqs_managed_sse_enabled = optional(bool, true)
}), {})
role_path = optional(string, null)
role_permissions_boundary = optional(string, null)
runtime = optional(string, null)
security_group_ids = optional(list(string), [])
subnet_ids = optional(list(string), [])
s3_bucket = optional(string, null)
s3_key = optional(string, null)
s3_object_version = optional(string, null)
sqs_build_queue = object({
url = string
arn = string
})
tags = optional(map(string), {})
timeout = optional(number, 30)
tracing_config = optional(object({
mode = optional(string, null)
capture_http_requests = optional(bool, false)
capture_error = optional(bool, false)
}), {})
zip = optional(string, null)
}) | n/a | yes |
+| [config](#input\_config) | Configuration for the spot termination watcher lambda function.object({
aws_partition = optional(string, null)
architecture = optional(string, null)
enable_organization_runners = bool
enable_enterprise_runners = bool
enterprise_slug = string
environment_variables = optional(map(string), {})
ghes_url = optional(string, null)
user_agent = optional(string, null)
github_app_parameters = object({
key_base64 = optional(map(string))
id = optional(map(string))
})
enterprise_pat = optional(map(string))
kms_key_arn = optional(string, null)
lambda_tags = optional(map(string), {})
log_level = optional(string, null)
logging_kms_key_id = optional(string, null)
logging_retention_in_days = optional(number, null)
memory_size = optional(number, null)
metrics = optional(object({
enable = optional(bool, false)
namespace = optional(string, null)
metric = optional(object({
enable_github_app_rate_limit = optional(bool, true)
enable_job_retry = optional(bool, true)
}), {})
}), {})
prefix = optional(string, null)
principals = optional(list(object({
type = string
identifiers = list(string)
})), [])
queue_encryption = optional(object({
kms_data_key_reuse_period_seconds = optional(number, null)
kms_master_key_id = optional(string, null)
sqs_managed_sse_enabled = optional(bool, true)
}), {})
role_path = optional(string, null)
role_permissions_boundary = optional(string, null)
runtime = optional(string, null)
security_group_ids = optional(list(string), [])
subnet_ids = optional(list(string), [])
s3_bucket = optional(string, null)
s3_key = optional(string, null)
s3_object_version = optional(string, null)
sqs_build_queue = object({
url = string
arn = string
})
tags = optional(map(string), {})
timeout = optional(number, 30)
tracing_config = optional(object({
mode = optional(string, null)
capture_http_requests = optional(bool, false)
capture_error = optional(bool, false)
}), {})
zip = optional(string, null)
}) | n/a | yes |
## Outputs
diff --git a/modules/runners/job-retry/main.tf b/modules/runners/job-retry/main.tf
index 9561c7db71..10e9300a3d 100644
--- a/modules/runners/job-retry/main.tf
+++ b/modules/runners/job-retry/main.tf
@@ -4,13 +4,16 @@ locals {
environment_variables = {
ENABLE_ORGANIZATION_RUNNERS = var.config.enable_organization_runners
+ ENABLE_ENTERPRISE_RUNNERS = var.config.enable_enterprise_runners
+ ENTERPRISE_SLUG = var.config.enterprise_slug
ENABLE_METRIC_JOB_RETRY = var.config.metrics.enable && var.config.metrics.metric.enable_job_retry
ENABLE_METRIC_GITHUB_APP_RATE_LIMIT = var.config.metrics.enable && var.config.metrics.metric.enable_github_app_rate_limit
GHES_URL = var.config.ghes_url
USER_AGENT = var.config.user_agent
JOB_QUEUE_SCALE_UP_URL = var.config.sqs_build_queue.url
- PARAMETER_GITHUB_APP_ID_NAME = var.config.github_app_parameters.id.name
- PARAMETER_GITHUB_APP_KEY_BASE64_NAME = var.config.github_app_parameters.key_base64.name
+ PARAMETER_GITHUB_APP_ID_NAME = var.config.enterprise_pat == null ? var.config.github_app_parameters.id.name : null
+ PARAMETER_GITHUB_APP_KEY_BASE64_NAME = var.config.enterprise_pat == null ? var.config.github_app_parameters.key_base64.name : null
+ PARAMETER_ENTERPRISE_PAT_NAME = var.config.enterprise_pat != null ? var.config.enterprise_pat.name : null
}
config = merge(var.config, {
@@ -66,6 +69,7 @@ resource "aws_iam_role_policy" "job_retry" {
sqs_job_retry_queue_arn = aws_sqs_queue.job_retry_check_queue.arn
github_app_id_arn = var.config.github_app_parameters.id.arn
github_app_key_base64_arn = var.config.github_app_parameters.key_base64.arn
+ enterprise_pat_arn = var.config.enterprise_pat
})
}
diff --git a/modules/runners/job-retry/policies/lambda.json b/modules/runners/job-retry/policies/lambda.json
index 591ec04790..24e02cd4c3 100644
--- a/modules/runners/job-retry/policies/lambda.json
+++ b/modules/runners/job-retry/policies/lambda.json
@@ -7,8 +7,15 @@
"ssm:GetParameter"
],
"Resource": [
- "${github_app_key_base64_arn}",
- "${github_app_id_arn}"
+%{ if github_app_key_base64_arn != null ~}
+ "${github_app_key_base64_arn}",
+%{ endif ~}
+%{ if github_app_id_arn != null ~}
+ "${github_app_id_arn}",
+%{ endif ~}
+%{ if enterprise_pat_arn != null ~}
+ "${enterprise_pat_arn}",
+%{ endif ~}
]
},
{
diff --git a/modules/runners/job-retry/variables.tf b/modules/runners/job-retry/variables.tf
index 4a8fe19fbf..927686ca67 100644
--- a/modules/runners/job-retry/variables.tf
+++ b/modules/runners/job-retry/variables.tf
@@ -6,10 +6,13 @@ variable "config" {
`architecture`: AWS Lambda architecture. Lambda functions using Graviton processors ('arm64') tend to have better price/performance than 'x86_64' functions.
`environment_variables`: Environment variables for the lambda.
`enable_organization_runners`: Enable organization runners.
+ `enable_enterprise_runners`: Enable enterprise runners.
+ `enterprise_slug`: GitHub enterprise slug.
`enable_metric`: Enable metric for the lambda. If `spot_warning` is set to true, the lambda will emit a metric when it detects a spot termination warning.
'ghes_url': Optional GitHub Enterprise Server URL.
'user_agent': Optional User-Agent header for GitHub API requests.
'github_app_parameters': Parameter Store for GitHub App Parameters.
+ 'enterprise_pat = string': Personal Access Token for GitHub Enterprise. If set, the lambda will use this PAT to authenticate with the GitHub API.
'kms_key_arn': Optional CMK Key ARN instead of using the default AWS managed key.
`lambda_principals`: Add extra principals to the role created for execution of the lambda, e.g. for local testing.
`lambda_tags`: Map of tags that will be added to created resources. By default resources will be tagged with name and environment.
@@ -38,13 +41,16 @@ variable "config" {
aws_partition = optional(string, null)
architecture = optional(string, null)
enable_organization_runners = bool
+ enable_enterprise_runners = bool
+ enterprise_slug = string
environment_variables = optional(map(string), {})
ghes_url = optional(string, null)
user_agent = optional(string, null)
github_app_parameters = object({
- key_base64 = map(string)
- id = map(string)
+ key_base64 = optional(map(string))
+ id = optional(map(string))
})
+ enterprise_pat = optional(map(string))
kms_key_arn = optional(string, null)
lambda_tags = optional(map(string), {})
log_level = optional(string, null)
diff --git a/modules/runners/policies/lambda-scale-down.json b/modules/runners/policies/lambda-scale-down.json
index d35be746b7..4b4fa3c3d1 100644
--- a/modules/runners/policies/lambda-scale-down.json
+++ b/modules/runners/policies/lambda-scale-down.json
@@ -49,8 +49,15 @@
"ssm:GetParameter"
],
"Resource": [
+%{ if github_app_key_base64_arn != null ~}
"${github_app_key_base64_arn}",
- "${github_app_id_arn}"
+%{ endif ~}
+%{ if github_app_id_arn != null ~}
+ "${github_app_id_arn}",
+%{ endif ~}
+%{ if enterprise_pat_arn != null ~}
+ "${enterprise_pat_arn}"
+%{ endif ~}
]
%{ if kms_key_arn != "" ~}
},
diff --git a/modules/runners/policies/lambda-scale-up.json b/modules/runners/policies/lambda-scale-up.json
index 1c6946b945..2444238d84 100644
--- a/modules/runners/policies/lambda-scale-up.json
+++ b/modules/runners/policies/lambda-scale-up.json
@@ -33,8 +33,15 @@
"ssm:GetParameter"
],
"Resource": [
+%{ if github_app_key_base64_arn != null ~}
"${github_app_key_base64_arn}",
+%{ endif ~}
+%{ if github_app_id_arn != null ~}
"${github_app_id_arn}",
+%{ endif ~}
+%{ if enterprise_pat_arn != null ~}
+ "${enterprise_pat_arn}",
+%{ endif ~}
"${ssm_config_path}/*"
]
},
diff --git a/modules/runners/pool.tf b/modules/runners/pool.tf
index 2762008ebf..8d70ef4aab 100644
--- a/modules/runners/pool.tf
+++ b/modules/runners/pool.tf
@@ -11,6 +11,8 @@ module "pool" {
}
user_agent = var.user_agent
github_app_parameters = var.github_app_parameters
+ enterprise_pat = var.enterprise_pat
+ enable_enterprise_runners = var.enable_enterprise_runners
instance_allocation_strategy = var.instance_allocation_strategy
instance_max_spot_price = var.instance_max_spot_price
instance_target_capacity_type = var.instance_target_capacity_type
@@ -61,5 +63,6 @@ module "pool" {
}
aws_partition = var.aws_partition
+ aws_region = var.aws_region
tracing_config = var.tracing_config
}
diff --git a/modules/runners/pool/README.md b/modules/runners/pool/README.md
index 052a8be60c..12113bf9fa 100644
--- a/modules/runners/pool/README.md
+++ b/modules/runners/pool/README.md
@@ -39,6 +39,7 @@ No modules.
| [aws_lambda_function.pool](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lambda_function) | resource |
| [aws_scheduler_schedule.pool](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/scheduler_schedule) | resource |
| [aws_scheduler_schedule_group.pool](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/scheduler_schedule_group) | resource |
+| [aws_caller_identity.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/caller_identity) | data source |
| [aws_iam_policy_document.lambda_assume_role_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
| [aws_iam_policy_document.lambda_xray](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
| [aws_iam_policy_document.scheduler](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
@@ -49,7 +50,8 @@ 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) | Lookup details in parent module. | object({
lambda = object({
log_level = string
logging_retention_in_days = number
logging_kms_key_id = 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)
})
tags = map(string)
ghes = object({
url = string
ssl_verify = string
})
github_app_parameters = object({
key_base64 = map(string)
id = map(string)
})
subnet_ids = list(string)
runner = object({
disable_runner_autoupdate = bool
ephemeral = bool
enable_jit_config = bool
enable_on_demand_failover_for_errors = list(string)
boot_time_in_minutes = number
labels = list(string)
launch_template = object({
name = string
})
group_name = string
name_prefix = string
pool_owner = string
role = object({
arn = string
})
})
instance_types = list(string)
instance_target_capacity_type = string
instance_allocation_strategy = string
instance_max_spot_price = string
prefix = string
pool = list(object({
schedule_expression = string
schedule_expression_timezone = string
size = number
}))
role_permissions_boundary = string
kms_key_arn = string
ami_kms_key_arn = string
ami_id_ssm_parameter_arn = string
role_path = string
ssm_token_path = string
ssm_config_path = string
ami_id_ssm_parameter_name = string
ami_id_ssm_parameter_read_policy_arn = string
arn_ssm_parameters_path_config = string
lambda_tags = map(string)
user_agent = string
}) | n/a | yes |
+| [aws\_region](#input\_aws\_region) | AWS region. | `string` | n/a | yes |
+| [config](#input\_config) | Lookup details in parent module. | object({
enable_enterprise_runners = optional(bool)
lambda = object({
log_level = string
logging_retention_in_days = number
logging_kms_key_id = 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)
})
tags = map(string)
ghes = object({
url = string
ssl_verify = string
})
github_app_parameters = object({
key_base64 = optional(map(string))
id = optional(map(string))
})
enterprise_pat = optional(map(string))
subnet_ids = list(string)
runner = object({
disable_runner_autoupdate = bool
ephemeral = bool
enable_jit_config = bool
enable_on_demand_failover_for_errors = list(string)
boot_time_in_minutes = number
labels = list(string)
launch_template = object({
name = string
})
group_name = string
name_prefix = string
pool_owner = string
role = object({
arn = string
})
})
instance_types = list(string)
instance_target_capacity_type = string
instance_allocation_strategy = string
instance_max_spot_price = string
prefix = string
pool = list(object({
schedule_expression = string
schedule_expression_timezone = string
size = number
}))
role_permissions_boundary = string
kms_key_arn = string
ami_kms_key_arn = string
ami_id_ssm_parameter_arn = string
role_path = string
ssm_token_path = string
ssm_config_path = string
ami_id_ssm_parameter_name = string
ami_id_ssm_parameter_read_policy_arn = string
arn_ssm_parameters_path_config = string
lambda_tags = map(string)
user_agent = string
}) | n/a | yes |
| [tracing\_config](#input\_tracing\_config) | Configuration for lambda tracing. | object({
mode = optional(string, null)
capture_http_requests = optional(bool, false)
capture_error = optional(bool, false)
}) | `{}` | no |
## Outputs
diff --git a/modules/runners/pool/main.tf b/modules/runners/pool/main.tf
index e141b22d25..db719c8d56 100644
--- a/modules/runners/pool/main.tf
+++ b/modules/runners/pool/main.tf
@@ -1,3 +1,5 @@
+data "aws_caller_identity" "current" {}
+
resource "aws_lambda_function" "pool" {
s3_bucket = var.config.lambda.s3_bucket != null ? var.config.lambda.s3_bucket : null
@@ -17,22 +19,25 @@ resource "aws_lambda_function" "pool" {
environment {
variables = {
- AMI_ID_SSM_PARAMETER_NAME = var.config.ami_id_ssm_parameter_name
- DISABLE_RUNNER_AUTOUPDATE = var.config.runner.disable_runner_autoupdate
- ENABLE_EPHEMERAL_RUNNERS = var.config.runner.ephemeral
- ENABLE_JIT_CONFIG = var.config.runner.enable_jit_config
- ENVIRONMENT = var.config.prefix
- GHES_URL = var.config.ghes.url
- USER_AGENT = var.config.user_agent
- INSTANCE_ALLOCATION_STRATEGY = var.config.instance_allocation_strategy
- INSTANCE_MAX_SPOT_PRICE = var.config.instance_max_spot_price
- INSTANCE_TARGET_CAPACITY_TYPE = var.config.instance_target_capacity_type
- INSTANCE_TYPES = join(",", var.config.instance_types)
- LAUNCH_TEMPLATE_NAME = var.config.runner.launch_template.name
- LOG_LEVEL = var.config.lambda.log_level
- NODE_TLS_REJECT_UNAUTHORIZED = var.config.ghes.url != null && !var.config.ghes.ssl_verify ? 0 : 1
- PARAMETER_GITHUB_APP_ID_NAME = var.config.github_app_parameters.id.name
- PARAMETER_GITHUB_APP_KEY_BASE64_NAME = var.config.github_app_parameters.key_base64.name
+ AMI_ID_SSM_PARAMETER_NAME = var.config.ami_id_ssm_parameter_name
+ DISABLE_RUNNER_AUTOUPDATE = var.config.runner.disable_runner_autoupdate
+ ENABLE_EPHEMERAL_RUNNERS = var.config.runner.ephemeral
+ ENABLE_JIT_CONFIG = var.config.runner.enable_jit_config
+ ENVIRONMENT = var.config.prefix
+ GHES_URL = var.config.ghes.url
+ USER_AGENT = var.config.user_agent
+ INSTANCE_ALLOCATION_STRATEGY = var.config.instance_allocation_strategy
+ INSTANCE_MAX_SPOT_PRICE = var.config.instance_max_spot_price
+ INSTANCE_TARGET_CAPACITY_TYPE = var.config.instance_target_capacity_type
+ INSTANCE_TYPES = join(",", var.config.instance_types)
+ LAUNCH_TEMPLATE_NAME = var.config.runner.launch_template.name
+ LOG_LEVEL = var.config.lambda.log_level
+ NODE_TLS_REJECT_UNAUTHORIZED = var.config.ghes.url != null && !var.config.ghes.ssl_verify ? 0 : 1
+ PARAMETER_GITHUB_APP_ID_NAME = var.config.enterprise_pat == null ? var.config.github_app_parameters.id.name : null
+ PARAMETER_GITHUB_APP_KEY_BASE64_NAME = var.config.enterprise_pat == null ? var.config.github_app_parameters.key_base64.name : null
+ PARAMETER_ENTERPRISE_PAT_NAME = var.config.enterprise_pat != null ? var.config.enterprise_pat.name : null
+ ENABLE_ENTERPRISE_RUNNERS = var.config.enable_enterprise_runners
+
POWERTOOLS_LOGGER_LOG_EVENT = var.config.lambda.log_level == "debug" ? "true" : "false"
RUNNER_BOOT_TIME_IN_MINUTES = var.config.runner.boot_time_in_minutes
RUNNER_LABELS = lower(join(",", var.config.runner.labels))
@@ -87,11 +92,14 @@ resource "aws_iam_role_policy" "pool" {
policy = templatefile("${path.module}/policies/lambda-pool.json", {
arn_ssm_parameters_path_config = var.config.arn_ssm_parameters_path_config
arn_runner_instance_role = var.config.runner.role.arn
- github_app_id_arn = var.config.github_app_parameters.id.arn
- github_app_key_base64_arn = var.config.github_app_parameters.key_base64.arn
+ github_app_id_arn = var.config.enterprise_pat == null ? var.config.github_app_parameters.id.arn : null
+ github_app_key_base64_arn = var.config.enterprise_pat == null ? var.config.github_app_parameters.key_base64.arn : null
+ enterprise_pat_arn = var.config.enterprise_pat != null ? var.config.enterprise_pat.arn : null
kms_key_arn = var.config.kms_key_arn
ami_kms_key_arn = var.config.ami_kms_key_arn
- ssm_ami_id_parameter_arn = var.config.ami_id_ssm_parameter_arn
+ ssm_config_path = "arn:${var.aws_partition}:ssm:${var.aws_region}:${data.aws_caller_identity.current.account_id}:parameter${var.config.ssm_config_path}"
+
+ ssm_ami_id_parameter_arn = var.config.ami_id_ssm_parameter_arn
})
}
diff --git a/modules/runners/pool/policies/lambda-pool.json b/modules/runners/pool/policies/lambda-pool.json
index b0360a825c..f6c827fe8b 100644
--- a/modules/runners/pool/policies/lambda-pool.json
+++ b/modules/runners/pool/policies/lambda-pool.json
@@ -54,8 +54,16 @@
"ssm:GetParameter"
],
"Resource": [
+%{ if github_app_key_base64_arn != null ~}
"${github_app_key_base64_arn}",
- "${github_app_id_arn}"
+%{ endif ~}
+%{ if github_app_id_arn != null ~}
+ "${github_app_id_arn}",
+%{ endif ~}
+%{ if enterprise_pat_arn != null ~}
+ "${enterprise_pat_arn}",
+%{ endif ~}
+ "${ssm_config_path}/*"
]
%{ if kms_key_arn != "" ~}
},
diff --git a/modules/runners/pool/variables.tf b/modules/runners/pool/variables.tf
index f1e841cde6..b593169833 100644
--- a/modules/runners/pool/variables.tf
+++ b/modules/runners/pool/variables.tf
@@ -1,6 +1,7 @@
variable "config" {
description = "Lookup details in parent module."
type = object({
+ enable_enterprise_runners = optional(bool)
lambda = object({
log_level = string
logging_retention_in_days = number
@@ -23,10 +24,11 @@ variable "config" {
ssl_verify = string
})
github_app_parameters = object({
- key_base64 = map(string)
- id = map(string)
+ key_base64 = optional(map(string))
+ id = optional(map(string))
})
- subnet_ids = list(string)
+ enterprise_pat = optional(map(string))
+ subnet_ids = list(string)
runner = object({
disable_runner_autoupdate = bool
ephemeral = bool
@@ -85,4 +87,9 @@ variable "tracing_config" {
default = {}
}
+variable "aws_region" {
+ description = "AWS region."
+ type = string
+}
+
diff --git a/modules/runners/scale-down.tf b/modules/runners/scale-down.tf
index d274e3d4f1..dde8a1c079 100644
--- a/modules/runners/scale-down.tf
+++ b/modules/runners/scale-down.tf
@@ -29,8 +29,9 @@ resource "aws_lambda_function" "scale_down" {
LOG_LEVEL = var.log_level
MINIMUM_RUNNING_TIME_IN_MINUTES = coalesce(var.minimum_running_time_in_minutes, local.min_runtime_defaults[var.runner_os])
NODE_TLS_REJECT_UNAUTHORIZED = var.ghes_url != null && !var.ghes_ssl_verify ? 0 : 1
- PARAMETER_GITHUB_APP_ID_NAME = var.github_app_parameters.id.name
- PARAMETER_GITHUB_APP_KEY_BASE64_NAME = var.github_app_parameters.key_base64.name
+ PARAMETER_GITHUB_APP_ID_NAME = var.enterprise_pat == null ? var.github_app_parameters.id.name : null
+ PARAMETER_GITHUB_APP_KEY_BASE64_NAME = var.enterprise_pat == null ? var.github_app_parameters.key_base64.name : null
+ PARAMETER_ENTERPRISE_PAT_NAME = var.enterprise_pat != null ? var.enterprise_pat.name : null
POWERTOOLS_LOGGER_LOG_EVENT = var.log_level == "debug" ? "true" : "false"
RUNNER_BOOT_TIME_IN_MINUTES = var.runner_boot_time_in_minutes
SCALE_DOWN_CONFIG = jsonencode(var.idle_config)
@@ -97,8 +98,9 @@ resource "aws_iam_role_policy" "scale_down" {
role = aws_iam_role.scale_down.name
policy = templatefile("${path.module}/policies/lambda-scale-down.json", {
environment = var.prefix
- github_app_id_arn = var.github_app_parameters.id.arn
- github_app_key_base64_arn = var.github_app_parameters.key_base64.arn
+ github_app_id_arn = var.enterprise_pat == null ? var.github_app_parameters.id.arn : null
+ github_app_key_base64_arn = var.enterprise_pat == null ? var.github_app_parameters.key_base64.arn : null
+ enterprise_pat_arn = var.enterprise_pat != null ? var.enterprise_pat.arn : null
kms_key_arn = local.kms_key_arn
})
}
diff --git a/modules/runners/scale-up.tf b/modules/runners/scale-up.tf
index 9230267c07..8ba9da8617 100644
--- a/modules/runners/scale-up.tf
+++ b/modules/runners/scale-up.tf
@@ -32,6 +32,8 @@ resource "aws_lambda_function" "scale_up" {
ENABLE_JOB_QUEUED_CHECK = local.enable_job_queued_check
ENABLE_METRIC_GITHUB_APP_RATE_LIMIT = var.metrics.enable && var.metrics.metric.enable_github_app_rate_limit
ENABLE_ORGANIZATION_RUNNERS = var.enable_organization_runners
+ ENABLE_ENTERPRISE_RUNNERS = var.enable_enterprise_runners
+ ENTERPRISE_SLUG = var.enterprise_slug
ENVIRONMENT = var.prefix
GHES_URL = var.ghes_url
USER_AGENT = var.user_agent
@@ -43,8 +45,9 @@ resource "aws_lambda_function" "scale_up" {
LOG_LEVEL = var.log_level
MINIMUM_RUNNING_TIME_IN_MINUTES = coalesce(var.minimum_running_time_in_minutes, local.min_runtime_defaults[var.runner_os])
NODE_TLS_REJECT_UNAUTHORIZED = var.ghes_url != null && !var.ghes_ssl_verify ? 0 : 1
- PARAMETER_GITHUB_APP_ID_NAME = var.github_app_parameters.id.name
- PARAMETER_GITHUB_APP_KEY_BASE64_NAME = var.github_app_parameters.key_base64.name
+ PARAMETER_GITHUB_APP_ID_NAME = var.enterprise_pat == null ? var.github_app_parameters.id.name : null
+ PARAMETER_GITHUB_APP_KEY_BASE64_NAME = var.enterprise_pat == null ? var.github_app_parameters.key_base64.name : null
+ PARAMETER_ENTERPRISE_PAT_NAME = var.enterprise_pat != null ? var.enterprise_pat.name : null
POWERTOOLS_LOGGER_LOG_EVENT = var.log_level == "debug" ? "true" : "false"
POWERTOOLS_METRICS_NAMESPACE = var.metrics.namespace
POWERTOOLS_TRACE_ENABLED = var.tracing_config.mode != null ? true : false
@@ -114,8 +117,9 @@ resource "aws_iam_role_policy" "scale_up" {
policy = templatefile("${path.module}/policies/lambda-scale-up.json", {
arn_runner_instance_role = aws_iam_role.runner.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
+ github_app_id_arn = var.enterprise_pat == null ? var.github_app_parameters.id.arn : null
+ github_app_key_base64_arn = var.enterprise_pat == null ? var.github_app_parameters.key_base64.arn : null
+ enterprise_pat_arn = var.enterprise_pat != null ? var.enterprise_pat.arn : null
ssm_config_path = "arn:${var.aws_partition}:ssm:${var.aws_region}:${data.aws_caller_identity.current.account_id}:parameter${var.ssm_paths.root}/${var.ssm_paths.config}"
kms_key_arn = local.kms_key_arn
ami_kms_key_arn = local.ami_kms_key_arn
diff --git a/modules/runners/variables.tf b/modules/runners/variables.tf
index a78231e7da..e527f14f42 100644
--- a/modules/runners/variables.tf
+++ b/modules/runners/variables.tf
@@ -219,6 +219,16 @@ variable "enable_organization_runners" {
type = bool
}
+variable "enable_enterprise_runners" {
+ description = "Register runners to organization, instead of repo or organization level"
+ type = bool
+}
+
+variable "enterprise_slug" {
+ description = "Enterprise slug"
+ type = string
+}
+
variable "github_app_parameters" {
description = "Parameter Store for GitHub App Parameters."
type = object({
@@ -227,6 +237,11 @@ variable "github_app_parameters" {
})
}
+variable "enterprise_pat" {
+ description = "GitHub Enterprise PAT to use for registering runners to the enterprise. This is only required when `enable_enterprise_runners` is set to true."
+ type = map(string)
+}
+
variable "lambda_scale_down_memory_size" {
description = "Memory size limit in MB for scale down lambda."
type = number
diff --git a/modules/ssm/README.md b/modules/ssm/README.md
index cb23d3aa87..f0c8eb95e8 100644
--- a/modules/ssm/README.md
+++ b/modules/ssm/README.md
@@ -29,12 +29,14 @@ No modules.
| [aws_ssm_parameter.github_app_id](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ssm_parameter) | resource |
| [aws_ssm_parameter.github_app_key_base64](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ssm_parameter) | resource |
| [aws_ssm_parameter.github_app_webhook_secret](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ssm_parameter) | resource |
+| [aws_ssm_parameter.github_enterprise_pat](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ssm_parameter) | resource |
## Inputs
| Name | Description | Type | Default | Required |
|------|-------------|------|---------|:--------:|
-| [github\_app](#input\_github\_app) | GitHub app parameters, see your github app. object({
key_base64 = optional(string)
key_base64_ssm = optional(object({
arn = string
name = string
}))
id = optional(string)
id_ssm = optional(object({
arn = string
name = string
}))
webhook_secret = optional(string)
webhook_secret_ssm = optional(object({
arn = string
name = string
}))
}) | n/a | yes |
+| [enterprise\_pat](#input\_enterprise\_pat) | GitHub enterprise PAT. Used only when enable\_enterprise\_runners is true. | `string` | `null` | no |
+| [github\_app](#input\_github\_app) | GitHub app parameters. | object({
key_base64 = optional(string)
key_base64_ssm = optional(object({
arn = string
name = string
}))
id = optional(string)
id_ssm = optional(object({
arn = string
name = string
}))
webhook_secret = optional(string)
webhook_secret_ssm = optional(object({
arn = string
name = string
}))
}) | n/a | yes |
| [kms\_key\_arn](#input\_kms\_key\_arn) | Optional CMK Key ARN to be used for Parameter Store. | `string` | `null` | no |
| [path\_prefix](#input\_path\_prefix) | The path prefix used for naming resources | `string` | n/a | yes |
| [tags](#input\_tags) | Map of tags that will be added to created resources. By default resources will be tagged with name and environment. | `map(string)` | `{}` | no |
diff --git a/modules/ssm/outputs.tf b/modules/ssm/outputs.tf
index 4017f6ab3d..62b1f5ff11 100644
--- a/modules/ssm/outputs.tf
+++ b/modules/ssm/outputs.tf
@@ -1,16 +1,20 @@
output "parameters" {
value = {
github_app_id = {
- name = var.github_app.id_ssm != null ? var.github_app.id_ssm.name : aws_ssm_parameter.github_app_id[0].name
- arn = var.github_app.id_ssm != null ? var.github_app.id_ssm.arn : aws_ssm_parameter.github_app_id[0].arn
+ name = var.github_app.id_ssm != null ? var.github_app.id_ssm.name : var.enterprise_pat != null ? null : aws_ssm_parameter.github_app_id[0].name
+ arn = var.github_app.id_ssm != null ? var.github_app.id_ssm.arn : var.enterprise_pat != null ? null : aws_ssm_parameter.github_app_id[0].arn
}
github_app_key_base64 = {
- name = var.github_app.key_base64_ssm != null ? var.github_app.key_base64_ssm.name : aws_ssm_parameter.github_app_key_base64[0].name
- arn = var.github_app.key_base64_ssm != null ? var.github_app.key_base64_ssm.arn : aws_ssm_parameter.github_app_key_base64[0].arn
+ name = var.github_app.key_base64_ssm != null ? var.github_app.key_base64_ssm.name : var.enterprise_pat != null ? null : aws_ssm_parameter.github_app_key_base64[0].name
+ arn = var.github_app.key_base64_ssm != null ? var.github_app.key_base64_ssm.arn : var.enterprise_pat != null ? null : aws_ssm_parameter.github_app_key_base64[0].arn
}
github_app_webhook_secret = {
name = var.github_app.webhook_secret_ssm != null ? var.github_app.webhook_secret_ssm.name : aws_ssm_parameter.github_app_webhook_secret[0].name
arn = var.github_app.webhook_secret_ssm != null ? var.github_app.webhook_secret_ssm.arn : aws_ssm_parameter.github_app_webhook_secret[0].arn
}
+ enterprise_pat = {
+ name = var.enterprise_pat != null ? aws_ssm_parameter.github_enterprise_pat[0].name : null
+ arn = var.enterprise_pat != null ? aws_ssm_parameter.github_enterprise_pat[0].arn : null
+ }
}
}
diff --git a/modules/ssm/ssm.tf b/modules/ssm/ssm.tf
index 3f13333e68..4b4f2969c1 100644
--- a/modules/ssm/ssm.tf
+++ b/modules/ssm/ssm.tf
@@ -1,5 +1,5 @@
resource "aws_ssm_parameter" "github_app_id" {
- count = var.github_app.id_ssm != null ? 0 : 1
+ count = var.github_app.id_ssm != null || var.enterprise_pat != null ? 0 : 1
name = "${var.path_prefix}/github_app_id"
type = "SecureString"
value = var.github_app.id
@@ -8,7 +8,7 @@ resource "aws_ssm_parameter" "github_app_id" {
}
resource "aws_ssm_parameter" "github_app_key_base64" {
- count = var.github_app.key_base64_ssm != null ? 0 : 1
+ count = var.github_app.key_base64_ssm != null || var.enterprise_pat != null ? 0 : 1
name = "${var.path_prefix}/github_app_key_base64"
type = "SecureString"
value = var.github_app.key_base64
@@ -24,3 +24,12 @@ resource "aws_ssm_parameter" "github_app_webhook_secret" {
key_id = local.kms_key_arn
tags = var.tags
}
+
+resource "aws_ssm_parameter" "github_enterprise_pat" {
+ count = var.enterprise_pat != null ? 1 : 0
+ name = "${var.path_prefix}/github_enterprise_pat"
+ type = "SecureString"
+ value = var.enterprise_pat
+ key_id = local.kms_key_arn
+ tags = var.tags
+}
diff --git a/modules/ssm/variables.tf b/modules/ssm/variables.tf
index 1eb796aea7..58956ba235 100644
--- a/modules/ssm/variables.tf
+++ b/modules/ssm/variables.tf
@@ -1,11 +1,14 @@
+variable "enterprise_pat" {
+ description = "GitHub enterprise PAT. Used only when enable_enterprise_runners is true."
+ type = string
+ default = null
+}
+
variable "github_app" {
description = <