diff --git a/README.md b/README.md
index bfa4fb19f7..cd756feec9 100644
--- a/README.md
+++ b/README.md
@@ -222,7 +222,7 @@ Talk to the forestkeepers in the `runners-channel` on Slack.
| [runners\_ssm\_housekeeper](#input\_runners\_ssm\_housekeeper) | Configuration for the SSM housekeeper lambda. This lambda deletes token / JIT config from SSM.
`schedule_expression`: is used to configure the schedule for the lambda.
`enabled`: enable or disable the lambda trigger via the EventBridge.
`lambda_memory_size`: lambda memery size limit.
`lambda_timeout`: timeout for the lambda in seconds.
`config`: configuration for the lambda function. Token path will be read by default from the module. |
object({|
schedule_expression = optional(string, "rate(1 day)")
enabled = optional(bool, true)
lambda_memory_size = optional(number, 512)
lambda_timeout = optional(number, 60)
config = object({
tokenPath = optional(string)
minimumDaysOld = optional(number, 1)
dryRun = optional(bool, false)
})
})
{| no | | [scale\_down\_schedule\_expression](#input\_scale\_down\_schedule\_expression) | Scheduler expression to check every x for scale down. | `string` | `"cron(*/5 * * * ? *)"` | no | | [scale\_up\_reserved\_concurrent\_executions](#input\_scale\_up\_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 | -| [ssm\_paths](#input\_ssm\_paths) | The root path used in SSM to store configuration and secrets. |
"config": {}
}
object({| `{}` | no | +| [ssm\_paths](#input\_ssm\_paths) | The root path used in SSM to store configuration and secrets. |
root = optional(string, "github-action-runners")
app = optional(string, "app")
runners = optional(string, "runners")
use_prefix = optional(bool, true)
})
object({| `{}` | no | | [state\_event\_rule\_binaries\_syncer](#input\_state\_event\_rule\_binaries\_syncer) | Option to disable EventBridge Lambda trigger for the binary syncer, useful to stop automatic updates of binary distribution | `string` | `"ENABLED"` | no | | [subnet\_ids](#input\_subnet\_ids) | List of subnets in which the action runner instances will be launched. The subnets need to exist in the configured VPC (`vpc_id`), and must reside in different availability zones (see https://github.com/philips-labs/terraform-aws-github-runner/issues/2904) | `list(string)` | n/a | yes | | [syncer\_lambda\_s3\_key](#input\_syncer\_lambda\_s3\_key) | S3 key for syncer lambda function. Required if using an S3 bucket to specify lambdas. | `string` | `null` | no | diff --git a/docs/configuration.md b/docs/configuration.md index 44ac5ef394..29980fc68f 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -22,7 +22,7 @@ The module uses the AWS System Manager Parameter Store to store configuration fo | `ssm_paths.root/var.prefix?/app/` | App secrets used by Lambda's | | `ssm_paths.root/var.prefix?/runners/config/
root = optional(string, "github-action-runners")
app = optional(string, "app")
runners = optional(string, "runners")
webhook = optional(string, "webhook")
use_prefix = optional(bool, true)
})
object({| n/a | yes | | [scale\_down\_schedule\_expression](#input\_scale\_down\_schedule\_expression) | Scheduler expression to check every x for scale down. | `string` | `"cron(*/5 * * * ? *)"` | no | | [scale\_up\_reserved\_concurrent\_executions](#input\_scale\_up\_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 | -| [sqs\_build\_queue](#input\_sqs\_build\_queue) | SQS queue to consume accepted build events. |
arn = string
id = string
key = string
})
object({| n/a | yes | -| [ssm\_housekeeper](#input\_ssm\_housekeeper) | Configuration for the SSM housekeeper lambda. This lambda deletes token / JIT config from SSM.
arn = string
})
object({|
schedule_expression = optional(string, "rate(1 day)")
state = optional(string, "ENABLED")
lambda_memory_size = optional(number, 512)
lambda_timeout = optional(number, 60)
config = object({
tokenPath = optional(string)
minimumDaysOld = optional(number, 1)
dryRun = optional(bool, false)
})
})
{| no | -| [ssm\_paths](#input\_ssm\_paths) | The root path used in SSM to store configuration and secreets. |
"config": {}
}
object({| n/a | yes | +| [ssm\_housekeeper](#input\_ssm\_housekeeper) | Configuration for the SSM housekeeper lambda. This lambda deletes token / JIT config from SSM.
root = string
tokens = string
config = string
})
object({|
schedule_expression = optional(string, "rate(1 day)")
state = optional(string, "ENABLED")
lambda_timeout = optional(number, 60)
config = object({
tokenPath = optional(string)
minimumDaysOld = optional(number, 1)
dryRun = optional(bool, false)
})
})
{| no | +| [ssm\_paths](#input\_ssm\_paths) | The root path used in SSM to store configuration and secrets. |
"config": {}
}
object({| n/a | yes | | [subnet\_ids](#input\_subnet\_ids) | List of subnets in which the action runners will be launched, the subnets needs to be subnets in the `vpc_id`. | `list(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. | `map(string)` | `{}` | no | | [tracing\_config](#input\_tracing\_config) | Configuration for lambda tracing. |
root = string
tokens = string
config = string
})
object({| `{}` | no | diff --git a/modules/runners/variables.tf b/modules/runners/variables.tf index 6df5e08fd8..01f5211a2d 100644 --- a/modules/runners/variables.tf +++ b/modules/runners/variables.tf @@ -574,7 +574,7 @@ variable "enable_user_data_debug_logging" { } variable "ssm_paths" { - description = "The root path used in SSM to store configuration and secreets." + description = "The root path used in SSM to store configuration and secrets." type = object({ root = string tokens = string diff --git a/modules/webhook/README.md b/modules/webhook/README.md index 8249fb110d..38d9ac43bc 100644 --- a/modules/webhook/README.md +++ b/modules/webhook/README.md @@ -38,12 +38,14 @@ yarn run dist |------|---------| | [terraform](#requirement\_terraform) | >= 1.3.0 | | [aws](#requirement\_aws) | ~> 5.27 | +| [null](#requirement\_null) | ~> 3.2 | ## Providers | Name | Version | |------|---------| | [aws](#provider\_aws) | ~> 5.27 | +| [null](#provider\_null) | ~> 3.2 | ## Modules @@ -67,6 +69,8 @@ No modules. | [aws_iam_role_policy_attachment.webhook_vpc_execution_role](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | | [aws_lambda_function.webhook](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lambda_function) | resource | | [aws_lambda_permission.webhook](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lambda_permission) | resource | +| [aws_ssm_parameter.runner_matcher_config](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ssm_parameter) | resource | +| [null_resource.github_app_parameters](https://registry.terraform.io/providers/hashicorp/null/latest/docs/resources/resource) | resource | | [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 | @@ -92,8 +96,9 @@ No modules. | [repository\_white\_list](#input\_repository\_white\_list) | List of github repository full names (owner/repo\_name) that will be allowed to use the github app. Leave empty for no filtering. | `list(string)` | `[]` | no | | [role\_path](#input\_role\_path) | The path that will be added to the role; if not set, the environment name will be used. | `string` | `null` | no | | [role\_permissions\_boundary](#input\_role\_permissions\_boundary) | Permissions boundary that will be added to the created role for the lambda. | `string` | `null` | no | -| [runner\_config](#input\_runner\_config) | SQS queue to publish accepted build events based on the runner type. When exact match is disabled the webhook accecpts the event if one of the workflow job labels is part of the matcher. The priority defines the order the matchers are applied. |
mode = optional(string, null)
capture_http_requests = optional(bool, false)
capture_error = optional(bool, false)
})
map(object({| n/a | yes | +| [runner\_matcher\_config](#input\_runner\_matcher\_config) | SQS queue to publish accepted build events based on the runner type. When exact match is disabled the webhook accepts the event if one of the workflow job labels is part of the matcher. The priority defines the order the matchers are applied. |
arn = string
id = string
fifo = bool
matcherConfig = object({
labelMatchers = list(list(string))
exactMatch = bool
priority = optional(number, 999)
})
}))
map(object({| n/a | yes | | [sqs\_workflow\_job\_queue](#input\_sqs\_workflow\_job\_queue) | SQS queue to monitor github events. |
arn = string
id = string
fifo = bool
matcherConfig = object({
labelMatchers = list(list(string))
exactMatch = bool
priority = optional(number, 999)
})
}))
object({| `null` | no | +| [ssm\_paths](#input\_ssm\_paths) | The root path used in SSM to store configuration and secrets. |
id = string
arn = string
})
object({| 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 | | [tracing\_config](#input\_tracing\_config) | Configuration for lambda tracing. |
root = string
webhook = string
})
object({| `{}` | no | | [webhook\_lambda\_apigateway\_access\_log\_settings](#input\_webhook\_lambda\_apigateway\_access\_log\_settings) | Access log settings for webhook API gateway. |
mode = optional(string, null)
capture_http_requests = optional(bool, false)
capture_error = optional(bool, false)
})
object({| `null` | no | diff --git a/modules/webhook/main.tf b/modules/webhook/main.tf index c49957efab..503332b563 100644 --- a/modules/webhook/main.tf +++ b/modules/webhook/main.tf @@ -55,3 +55,10 @@ resource "aws_apigatewayv2_integration" "webhook" { integration_method = "POST" integration_uri = aws_lambda_function.webhook.invoke_arn } + + +resource "aws_ssm_parameter" "runner_matcher_config" { + name = "${var.ssm_paths.root}/${var.ssm_paths.webhook}/runner-matcher-config" + type = "String" + value = jsonencode(local.runner_matcher_config_sorted) +} diff --git a/modules/webhook/policies/lambda-ssm.json b/modules/webhook/policies/lambda-ssm.json index efef0b907d..23864db305 100644 --- a/modules/webhook/policies/lambda-ssm.json +++ b/modules/webhook/policies/lambda-ssm.json @@ -3,11 +3,10 @@ "Statement": [ { "Effect": "Allow", - "Action": [ - "ssm:GetParameter" - ], + "Action": ["ssm:GetParameter"], "Resource": [ - "${github_app_webhook_secret_arn}" + "${github_app_webhook_secret_arn}", + "${parameter_runner_matcher_config_arn}" ] } ] diff --git a/modules/webhook/variables.tf b/modules/webhook/variables.tf index 38960b1683..c4ed409585 100644 --- a/modules/webhook/variables.tf +++ b/modules/webhook/variables.tf @@ -22,8 +22,8 @@ variable "tags" { default = {} } -variable "runner_config" { - description = "SQS queue to publish accepted build events based on the runner type. When exact match is disabled the webhook accecpts the event if one of the workflow job labels is part of the matcher. The priority defines the order the matchers are applied." +variable "runner_matcher_config" { + description = "SQS queue to publish accepted build events based on the runner type. When exact match is disabled the webhook accepts the event if one of the workflow job labels is part of the matcher. The priority defines the order the matchers are applied." type = map(object({ arn = string id = string @@ -35,7 +35,7 @@ variable "runner_config" { }) })) validation { - condition = try(var.runner_config.matcherConfig.priority, 999) >= 0 && try(var.runner_config.matcherConfig.priority, 999) < 1000 + condition = try(var.runner_matcher_config.matcherConfig.priority, 999) >= 0 && try(var.runner_matcher_config.matcherConfig.priority, 999) < 1000 error_message = "The priority of the matcher must be between 0 and 999." } } @@ -186,3 +186,11 @@ variable "tracing_config" { }) default = {} } + +variable "ssm_paths" { + description = "The root path used in SSM to store configuration and secrets." + type = object({ + root = string + webhook = string + }) +} diff --git a/modules/webhook/versions.tf b/modules/webhook/versions.tf index 1df1926c45..d780c7775c 100644 --- a/modules/webhook/versions.tf +++ b/modules/webhook/versions.tf @@ -6,5 +6,10 @@ terraform { source = "hashicorp/aws" version = "~> 5.27" } + + null = { + source = "hashicorp/null" + version = "~> 3.2" + } } } diff --git a/modules/webhook/webhook.tf b/modules/webhook/webhook.tf index e6388f8767..6b90839054 100644 --- a/modules/webhook/webhook.tf +++ b/modules/webhook/webhook.tf @@ -1,9 +1,9 @@ locals { # config with combined key and order - runner_config = { for k, v in var.runner_config : format("%03d-%s", v.matcherConfig.priority, k) => merge(v, { key = k }) } + runner_matcher_config = { for k, v in var.runner_matcher_config : format("%03d-%s", v.matcherConfig.priority, k) => merge(v, { key = k }) } # sorted list - runner_config_sorted = [for k in sort(keys(local.runner_config)) : local.runner_config[k]] + runner_matcher_config_sorted = [for k in sort(keys(local.runner_matcher_config)) : local.runner_matcher_config[k]] } @@ -30,8 +30,8 @@ resource "aws_lambda_function" "webhook" { POWERTOOLS_TRACER_CAPTURE_ERROR = var.tracing_config.capture_error PARAMETER_GITHUB_APP_WEBHOOK_SECRET = var.github_app_parameters.webhook_secret.name REPOSITORY_WHITE_LIST = jsonencode(var.repository_white_list) - RUNNER_CONFIG = jsonencode(local.runner_config_sorted) SQS_WORKFLOW_JOB_QUEUE = try(var.sqs_workflow_job_queue, null) != null ? var.sqs_workflow_job_queue.id : "" + PARAMETER_RUNNER_MATCHER_CONFIG_PATH = aws_ssm_parameter.runner_matcher_config.name } } @@ -51,6 +51,9 @@ resource "aws_lambda_function" "webhook" { mode = var.tracing_config.mode } } + lifecycle { + replace_triggered_by = [aws_ssm_parameter.runner_matcher_config, null_resource.github_app_parameters] + } } resource "aws_cloudwatch_log_group" "webhook" { @@ -66,6 +69,15 @@ resource "aws_lambda_permission" "webhook" { function_name = aws_lambda_function.webhook.function_name principal = "apigateway.amazonaws.com" source_arn = "${aws_apigatewayv2_api.webhook.execution_arn}/*/*/${local.webhook_endpoint}" + lifecycle { + replace_triggered_by = [aws_ssm_parameter.runner_matcher_config, null_resource.github_app_parameters] + } +} + +resource "null_resource" "github_app_parameters" { + triggers = { + github_app_webhook_secret = var.github_app_parameters.webhook_secret.name + } } data "aws_iam_policy_document" "lambda_assume_role_policy" { @@ -106,7 +118,7 @@ resource "aws_iam_role_policy" "webhook_sqs" { role = aws_iam_role.webhook_lambda.name policy = templatefile("${path.module}/policies/lambda-publish-sqs-policy.json", { - sqs_resource_arns = jsonencode([for k, v in var.runner_config : v.arn]) + sqs_resource_arns = jsonencode([for k, v in var.runner_matcher_config : v.arn]) kms_key_arn = var.kms_key_arn != null ? var.kms_key_arn : "" }) } @@ -127,7 +139,8 @@ resource "aws_iam_role_policy" "webhook_ssm" { role = aws_iam_role.webhook_lambda.name policy = templatefile("${path.module}/policies/lambda-ssm.json", { - github_app_webhook_secret_arn = var.github_app_parameters.webhook_secret.arn, + github_app_webhook_secret_arn = var.github_app_parameters.webhook_secret.arn, + parameter_runner_matcher_config_arn = aws_ssm_parameter.runner_matcher_config.arn }) } diff --git a/variables.tf b/variables.tf index 87a81bc5f0..8bb5cfa28d 100644 --- a/variables.tf +++ b/variables.tf @@ -773,6 +773,7 @@ variable "ssm_paths" { root = optional(string, "github-action-runners") app = optional(string, "app") runners = optional(string, "runners") + webhook = optional(string, "webhook") use_prefix = optional(bool, true) }) default = {}
destination_arn = string
format = string
})