Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
101 changes: 53 additions & 48 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,18 +1,17 @@
# terraform-aws-github-ops-app

Terraform module that deploys the [GitHub Ops App](https://github.com/cruxstack/github-ops-app)
to AWS Lambda with API Gateway for webhook handling.
Terraform module that deploys the
[GitHub Ops App](https://github.com/cruxstack/github-ops-app) to AWS Lambda with
API Gateway for webhook handling.

## Features

- **Okta Group Sync** - Automatically syncs Okta groups to GitHub teams based
on configurable rules
- **Okta Group Sync** - Automatically syncs Okta groups to GitHub teams based on
configurable rules
- **Orphaned User Detection** - Identifies org members not in any synced Okta
teams
- **PR Compliance Monitoring** - Detects when PRs bypass branch protection
rules
- **Automatic Reconciliation** - Detects external team changes and triggers
sync
- **PR Compliance Monitoring** - Detects when PRs bypass branch protection rules
- **Automatic Reconciliation** - Detects external team changes and triggers sync
- **Slack Notifications** - Rich messages for violations and sync reports

## Usage
Expand Down Expand Up @@ -74,7 +73,7 @@ module "github_ops_app" {

## Quick Start

- **Create a GitHub App** with the required permissions
- **Create a GitHub App** with the required permissions
- [GitHub Ops App documentation](https://github.com/cruxstack/github-ops-app))
- **Deploy this module** with your GitHub App credentials
- **Configure the webhook URL** in your GitHub App settings using the
Expand All @@ -89,22 +88,22 @@ module "github_ops_app" {

## Inputs

| Name | Description | Type | Default | Required |
|-------------------------------|------------------------------------------------------------------------------|----------------|------------------------------------------------------|:--------:|
| `admin_token_config` | Configuration for admin token protecting some endpoints | `object` | `{ enabled = true }` | no |
| `api_gateway_config` | Configuration for the API Gateway | `object` | `{}` | no |
| `bot_force_rebuild_id` | ID to force rebuilding the Lambda function source code | `string` | `""` | no |
| `bot_repo` | GitHub repository URL for the GitHub Ops App source code | `string` | `"https://github.com/cruxstack/github-ops-app.git"` | no |
| `bot_version` | Version of the GitHub Ops App to use (`latest` or specific tag like `v0.1.0`)| `string` | `"latest"` | no |
| `github_app_config` | GitHub App configuration for authentication and webhook handling | `object` | n/a | yes |
| `lambda_config` | Configuration for the Lambda function | `object` | `{}` | no |
| `lambda_environment_variables`| Additional environment variables for the Lambda function | `map(string)` | `{}` | no |
| `lambda_log_retention_days` | Number of days to retain Lambda function logs | `number` | `30` | no |
| `okta_config` | Okta configuration for user and group synchronization | `object` | `{}` | no |
| `okta_sync_schedule` | EventBridge schedule configuration for automatic Okta sync | `object` | `{}` | no |
| `pr_compliance_config` | Configuration for PR compliance monitoring | `object` | `{}` | no |
| `slack_config` | Slack integration configuration for notifications | `object` | `{}` | no |
| `ssm_parameter_arns` | List of SSM Parameter Store ARNs for secrets retrieval | `list(string)` | `[]` | no |
| Name | Description | Type | Default | Required |
| ------------------------------ | ----------------------------------------------------------------------------- | -------------- | --------------------------------------------------- | :------: |
| `admin_token_config` | Configuration for admin token protecting some endpoints | `object` | `{ enabled = true }` | no |
| `api_gateway_config` | Configuration for the API Gateway | `object` | `{}` | no |
| `bot_force_rebuild_id` | ID to force rebuilding the Lambda function source code | `string` | `""` | no |
| `bot_repo` | GitHub repository URL for the GitHub Ops App source code | `string` | `"https://github.com/cruxstack/github-ops-app.git"` | no |
| `bot_version` | Version of the GitHub Ops App to use (`latest` or specific tag like `v0.1.0`) | `string` | `"latest"` | no |
| `github_app_config` | GitHub App configuration for authentication and webhook handling | `object` | n/a | yes |
| `lambda_config` | Configuration for the Lambda function | `object` | `{}` | no |
| `lambda_environment_variables` | Additional environment variables for the Lambda function | `map(string)` | `{}` | no |
| `lambda_log_retention_days` | Number of days to retain Lambda function logs | `number` | `30` | no |
| `okta_config` | Okta configuration for user and group synchronization | `object` | `{}` | no |
| `okta_sync_schedule` | EventBridge schedule configuration for automatic Okta sync | `object` | `{}` | no |
| `pr_compliance_config` | Configuration for PR compliance monitoring | `object` | `{}` | no |
| `slack_config` | Slack integration configuration for notifications | `object` | `{}` | no |
| `ssm_parameter_arns` | List of SSM Parameter Store ARNs for secrets retrieval | `list(string)` | `[]` | no |

### GitHub App Config

Expand Down Expand Up @@ -161,7 +160,9 @@ sync_rules = [
]
```

See the [GitHub Ops App documentation](https://github.com/cruxstack/github-ops-app/blob/main/docs/okta-setup.md#step-10-configure-sync-rules) for detailed sync rule configuration.
See the
[GitHub Ops App documentation](https://github.com/cruxstack/github-ops-app/blob/main/docs/okta-setup.md#step-10-configure-sync-rules)
for detailed sync rule configuration.

### Lambda Config

Expand All @@ -188,7 +189,8 @@ slack_config = {
}
```

Per-notification channels are optional and fall back to the default `channel` if not specified.
Per-notification channels are optional and fall back to the default `channel` if
not specified.

### Admin Token Config

Expand All @@ -199,29 +201,32 @@ admin_token_config = {
}
```

When enabled, requests to `/server/*` and `/scheduled/*` endpoints require an `Authorization: Bearer <token>` header. If no token is provided, a secure 32-character token is automatically generated and available via the `admin_token` output.
When enabled, requests to `/server/*` and `/scheduled/*` endpoints require an
`Authorization: Bearer <token>` header. If no token is provided, a secure
32-character token is automatically generated and available via the
`admin_token` output.

## Outputs

| Name | Description |
|----------------------------------|------------------------------------------------------|
| `lambda_function_arn` | ARN of the GitHub Ops App Lambda function |
| `lambda_function_name` | Name of the GitHub Ops App Lambda function |
| `lambda_function_qualified_arn` | Qualified ARN of the Lambda function |
| `lambda_function_invoke_arn` | Invoke ARN of the Lambda function |
| `lambda_role_arn` | ARN of the IAM role used by the Lambda function |
| `lambda_role_name` | Name of the IAM role used by the Lambda function |
| `cloudwatch_log_group_name` | Name of the CloudWatch Log Group |
| `cloudwatch_log_group_arn` | ARN of the CloudWatch Log Group |
| `api_gateway_id` | ID of the API Gateway HTTP API |
| `api_gateway_arn` | ARN of the API Gateway HTTP API |
| `api_gateway_endpoint` | Base URL of the API Gateway |
| `api_gateway_execution_arn` | Execution ARN of the API Gateway |
| `webhook_url` | Full webhook URL to configure in GitHub App settings |
| `webhook_secret` | Webhook secret to configure in GitHub App |
| `admin_token` | Admin token for `/server/*` and `/scheduled/*` endpoints |
| `eventbridge_rule_arn` | ARN of the EventBridge rule for scheduled Okta sync |
| `eventbridge_rule_name` | Name of the EventBridge rule |
| Name | Description |
| ------------------------------- | -------------------------------------------------------- |
| `lambda_function_arn` | ARN of the GitHub Ops App Lambda function |
| `lambda_function_name` | Name of the GitHub Ops App Lambda function |
| `lambda_function_qualified_arn` | Qualified ARN of the Lambda function |
| `lambda_function_invoke_arn` | Invoke ARN of the Lambda function |
| `lambda_role_arn` | ARN of the IAM role used by the Lambda function |
| `lambda_role_name` | Name of the IAM role used by the Lambda function |
| `cloudwatch_log_group_name` | Name of the CloudWatch Log Group |
| `cloudwatch_log_group_arn` | ARN of the CloudWatch Log Group |
| `api_gateway_id` | ID of the API Gateway HTTP API |
| `api_gateway_arn` | ARN of the API Gateway HTTP API |
| `api_gateway_endpoint` | Base URL of the API Gateway |
| `api_gateway_execution_arn` | Execution ARN of the API Gateway |
| `webhook_url` | Full webhook URL to configure in GitHub App settings |
| `webhook_secret` | Webhook secret to configure in GitHub App |
| `admin_token` | Admin token for `/server/*` and `/scheduled/*` endpoints |
| `eventbridge_rule_arn` | ARN of the EventBridge rule for scheduled Okta sync |
| `eventbridge_rule_name` | Name of the EventBridge rule |

## Architecture

Expand Down Expand Up @@ -259,7 +264,7 @@ When enabled, requests to `/server/*` and `/scheduled/*` endpoints require an `A
## Requirements

| Name | Version |
|-----------|---------|
| --------- | ------- |
| terraform | >= 1.3 |
| aws | >= 5.0 |

Expand Down
32 changes: 26 additions & 6 deletions examples/complete/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -69,14 +69,28 @@ module "github_ops_app" {
monitored_branches = ["main", "master", "release/*"]
}

# security alerts monitoring
security_alerts_config = {
enabled = true
min_age_days = 30 # only report alerts older than 30 days
min_severity = "high" # minimum severity: critical, high, medium, low
}

# schedule security alerts check daily
security_alerts_schedule = {
enabled = true
schedule_expression = "rate(24 hours)"
}

# slack notifications (optional)
slack_config = {
enabled = true
token = var.slack_token
channel = var.slack_channel
channel_pr_bypass = var.slack_channel_pr_bypass # optional: override for PR bypass alerts
channel_okta_sync = var.slack_channel_okta_sync # optional: override for sync reports
channel_orphaned_users = var.slack_channel_orphaned_users # optional: override for orphaned user alerts
enabled = true
token = var.slack_token
channel = var.slack_channel
channel_pr_bypass = var.slack_channel_pr_bypass # optional: override for PR bypass alerts
channel_okta_sync = var.slack_channel_okta_sync # optional: override for sync reports
channel_orphaned_users = var.slack_channel_orphaned_users # optional: override for orphaned user alerts
channel_security_alerts = var.slack_channel_security_alerts # optional: override for security alerts
}

# lambda configuration
Expand Down Expand Up @@ -184,6 +198,12 @@ variable "slack_channel_orphaned_users" {
default = ""
}

variable "slack_channel_security_alerts" {
type = string
description = "Slack channel ID for security alerts (optional, falls back to slack_channel)"
default = ""
}

# -----------------------------------------------------------------------------
# outputs
# -----------------------------------------------------------------------------
Expand Down
42 changes: 42 additions & 0 deletions main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,14 @@ locals {
# pr compliance config
APP_PR_COMPLIANCE_ENABLED = tostring(var.pr_compliance_config.enabled)
APP_PR_MONITORED_BRANCHES = var.pr_compliance_config.enabled ? join(",", var.pr_compliance_config.monitored_branches) : ""

# security alerts config
APP_SECURITY_ALERTS_ENABLED = tostring(var.security_alerts_config.enabled)
},
var.security_alerts_config.enabled ? {
APP_SECURITY_ALERTS_MIN_AGE_DAYS = tostring(var.security_alerts_config.min_age_days)
APP_SECURITY_ALERTS_MIN_SEVERITY = var.security_alerts_config.min_severity
} : {},
# admin token config (conditional)
local.admin_token != "" ? { APP_ADMIN_TOKEN = local.admin_token } : {},
# okta config (conditional)
Expand All @@ -48,6 +55,7 @@ locals {
},
var.slack_config.channel_okta_sync != "" ? { APP_SLACK_CHANNEL_OKTA_SYNC = var.slack_config.channel_okta_sync } : {},
var.slack_config.channel_orphaned_users != "" ? { APP_SLACK_CHANNEL_ORPHANED_USERS = var.slack_config.channel_orphaned_users } : {},
var.slack_config.channel_security_alerts != "" ? { APP_SLACK_CHANNEL_SECURITY_ALERTS = var.slack_config.channel_security_alerts } : {},
var.slack_config.channel_pr_bypass != "" ? {
APP_SLACK_CHANNEL_PR_BYPASS = var.slack_config.channel_pr_bypass
APP_SLACK_FOOTER_NOTE_PR_BYPASS = var.pr_compliance_config.slack_footer_note
Expand Down Expand Up @@ -251,6 +259,40 @@ resource "aws_lambda_permission" "eventbridge" {
source_arn = aws_cloudwatch_event_rule.okta_sync[0].arn
}

resource "aws_cloudwatch_event_rule" "security_alerts" {
count = local.enabled && var.security_alerts_schedule.enabled ? 1 : 0

name = "${module.this.id}-security-alerts"
description = "Scheduled trigger for GitHub security alerts monitoring"
schedule_expression = var.security_alerts_schedule.schedule_expression
tags = module.this.tags
}

resource "aws_cloudwatch_event_target" "security_alerts" {
count = local.enabled && var.security_alerts_schedule.enabled ? 1 : 0

rule = aws_cloudwatch_event_rule.security_alerts[0].name
target_id = "SecurityAlertsLambda"
arn = aws_lambda_function.this[0].arn

input = jsonencode({
path = "/scheduled/security-alerts"
httpMethod = "POST"
headers = {}
body = ""
})
}

resource "aws_lambda_permission" "eventbridge_security_alerts" {
count = local.enabled && var.security_alerts_schedule.enabled ? 1 : 0

statement_id = "AllowExecutionFromEventBridgeSecurityAlerts"
action = "lambda:InvokeFunction"
function_name = aws_lambda_function.this[0].function_name
principal = "events.amazonaws.com"
source_arn = aws_cloudwatch_event_rule.security_alerts[0].arn
}

# ---------------------------------------------------------------------- iam ---

resource "aws_iam_role" "this" {
Expand Down
10 changes: 10 additions & 0 deletions outputs.tf
Original file line number Diff line number Diff line change
Expand Up @@ -94,3 +94,13 @@ output "eventbridge_rule_name" {
description = "Name of the EventBridge rule for scheduled Okta sync (if enabled)"
value = try(aws_cloudwatch_event_rule.okta_sync[0].name, null)
}

output "eventbridge_security_alerts_rule_arn" {
description = "ARN of the EventBridge rule for scheduled security alerts monitoring (if enabled)"
value = try(aws_cloudwatch_event_rule.security_alerts[0].arn, null)
}

output "eventbridge_security_alerts_rule_name" {
description = "Name of the EventBridge rule for scheduled security alerts monitoring (if enabled)"
value = try(aws_cloudwatch_event_rule.security_alerts[0].name, null)
}
Loading