Skip to content

Commit 6e71c1a

Browse files
authored
Support new GitHub bot deployment (#15)
* initial commit * update shared resources name patterns * nodejs20.x + update default name pattern * fmt * enhance function name variable to api gateway * improve secrets management * example + readme * fix example * update readme + mvoe logic to locals * github source code * formatting * update all sources codes * update gitlab source code * add changelog + cr changes * update outputs & readme * remove SPECTRAL_DSN * rename to api_triggered_function_name * improve role name * add HOME to default variables * make sure HOME is set * lambda module take var from var * formatting * GH bot 2.0.4 * update date in change log
1 parent b1f91e0 commit 6e71c1a

27 files changed

+326
-145
lines changed

.gitignore

+2-1
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,5 @@ override.tf.json
1010
*_override.tf
1111
*_override.tf.json
1212
.terraformrc
13-
terraform.rc
13+
terraform.rc
14+
.DS_Store

CHANGELOG.md

+59
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
# Change Log
2+
3+
All notable changes to this project will be documented in this file.
4+
5+
## [3.0.0] - 2024-06-25
6+
7+
### Added
8+
9+
- Support to GitHub bot 2.x deployment integration
10+
- Enable running multiple bot instances of the same type in a single region
11+
- Enable setting a custom pattern for all the resources created by the module
12+
- Enable setting a path to the lambda source code (Zip file)
13+
14+
### Changed
15+
16+
- Lambdas runtime upgraded to node20.x
17+
18+
## [2.1.0] - 2023-08-16
19+
20+
### Added
21+
22+
- Support hardening & engines flag
23+
24+
## [2.0.0] - 2023-06-18
25+
26+
### Changed
27+
28+
- GitLab's integration infrastructure is now based on multiple lambda functions to make sure the response is being sent to GitLab in less than 10 seconds
29+
30+
## [1.1.1] - 2023-05-31
31+
32+
### Added
33+
34+
- Option to pull the secrets required for the GitLab bot to accessed from AWS secrets manager
35+
36+
## [1.1.0] - 2022-12-11
37+
38+
### Changed
39+
40+
- New versions of GitLab and TFC using new Spectral severities
41+
42+
## [1.0.2] - 2022-10-23
43+
44+
### Added
45+
46+
- Support for Jira integration
47+
- Support for GitLab integration
48+
49+
## [1.0.1] - 2022-10-05
50+
51+
### Changed
52+
53+
- Bots are now downloading the latest Spectral scanner version instead of accessing the scanner through a lambda layer
54+
55+
## [1.0.0] - 2022-09-11
56+
57+
### Added
58+
59+
- Added support for Terraform cloud integration

README.md

+60-35
Original file line numberDiff line numberDiff line change
@@ -6,42 +6,60 @@ Terraform configuration used to create the required AWS resources for integratin
66

77
## Requirements
88

9-
| Name | Version |
10-
| ----------- | ----------- |
11-
| [terraform](https://www.terraform.io/downloads) | >= 1.3.0 |
9+
10+
| Name | Version |
11+
|------|---------|
12+
| <a name="requirement_terraform"></a> [terraform](#requirement\_terraform) | >= 1.3.0 |
13+
| <a name="requirement_aws"></a> [aws](#requirement\_aws) | >= 5.26.0 |
1214

1315
## Providers
1416

15-
| Name | Version |
16-
| ----------- | ----------- |
17-
| [aws](https://registry.terraform.io/providers/hashicorp/aws/latest/docs) | >= 4.37.0 |
17+
| Name | Version |
18+
|------|---------|
19+
| <a name="provider_aws"></a> [aws](#provider\_aws) | >= 5.26.0 |
20+
| <a name="provider_random"></a> [random](#provider\_random) | n/a |
21+
22+
## Modules
23+
24+
| Name | Source | Version |
25+
|------|--------|---------|
26+
| <a name="module_api_gateway"></a> [api\_gateway](#module\_api\_gateway) | ./modules/api_gateway | n/a |
27+
| <a name="module_backend_lambda_function"></a> [backend\_lambda\_function](#module\_backend\_lambda\_function) | ./modules/lambda | n/a |
28+
| <a name="module_frontend_lambda_function"></a> [frontend\_lambda\_function](#module\_frontend\_lambda\_function) | ./modules/lambda | n/a |
29+
| <a name="module_lambda_function"></a> [lambda\_function](#module\_lambda\_function) | ./modules/lambda | n/a |
30+
| <a name="module_lambda_role"></a> [lambda\_role](#module\_lambda\_role) | ./modules/role | n/a |
31+
| <a name="module_secrets_manager"></a> [secrets\_manager](#module\_secrets\_manager) | ./modules/secrets_manager | n/a |
32+
1833

1934
## Inputs
2035

21-
| Name | Description | Type | Default | Required |
22-
| ----------- | ----------- | ----------- | ----------- | ----------- |
23-
| `environment` | The target environment name for deployment | `string` | `prod` | No |
24-
| `integration_type` | Spectral integration type (A unique phrase describing the integration) - Available values: `terraform`, `jira` and `gitlab` | `string` | N/A | Yes |
25-
| [`env_vars`](#env_vars) | Extendable object contains all required environment variables required for the integration. | `map(string)` | N/A | No |
26-
| [`global_tags`](#global_tags) | Tags to be applied on every newly created resource. | `map(string)` | ```{ BusinessUnit = "Spectral" }``` | No |
27-
| [`tags`](#tags) | Tags to be applied on concrete resources | `map(map(string))` | ```{ iam = { } lambda = { } api_gateway = { } }``` | No |
28-
| `lambda_enable_logs` | Specifies if Lambda should have CloudWatch a dedicated logs group. | `bool` | `false` | No |
29-
| `lambda_logs_retention_in_days` | Specifies the number of days you want to retain log events in the specified log group. | `number` | `30` | No |
30-
| `lambda_function_timeout` | Amount of time your Lambda Function has to run in seconds. | `number` | 300 | No |
31-
| `lambda_function_memory_size` | Amount of memory in MB your Lambda Function can use at runtime. | `number` | 1024 | No |
32-
| `lambda_publish` | Whether to publish creation/change as new Lambda Function Version. | `bool` | `false` | No |
33-
| `store_secret_in_secrets_manager` | Whether to store secrets values on a vault (currently supporting AWS secrets manager on GitLab integration). | `bool` | `false` | No |
36+
| Name | Description | Type | Default | Required |
37+
|------|-------------|------|---------|:--------:|
38+
| <a name="input_backend_lambda_source_code_path"></a> [backend\_lambda\_source\_code\_path](#input\_backend\_lambda\_source\_code\_path) | Path to the lambda source code zip file of the backend lambda | `string` | `null` | no |
39+
| <a name="input_env_vars"></a> [env\_vars](#input\_env\_vars) | Extendable object contains all required environment variables required for the integration. | `map(string)` | <pre>{<br> "CHECK_POLICY": "Fail on errors only",<br> "SPECTRAL_DSN": ""<br>}</pre> | no |
40+
| <a name="input_environment"></a> [environment](#input\_environment) | The target environment name for deployment. | `string` | `"prod"` | no |
41+
| <a name="input_frontend_lambda_source_code_path"></a> [frontend\_lambda\_source\_code\_path](#input\_frontend\_lambda\_source\_code\_path) | Path to the lambda source code zip file of the frontend lambda | `string` | `null` | no |
42+
| <a name="input_gateway_api_integration_timeout_milliseconds"></a> [gateway\_api\_integration\_timeout\_milliseconds](#input\_gateway\_api\_integration\_timeout\_milliseconds) | Timeout for the API Gateway to wait for lambda response | `number` | `29000` | no |
43+
| <a name="input_global_tags"></a> [global\_tags](#input\_global\_tags) | A list of tags to apply on all newly created resources. | `map(string)` | <pre>{<br> "BusinessUnit": "Spectral"<br>}</pre> | no |
44+
| <a name="input_integration_type"></a> [integration\_type](#input\_integration\_type) | Spectral integration type (A unique phrase describing the integration) - Available values: `github`, `terraform`, `jira` and `gitlab` | `string` | n/a | yes |
45+
| <a name="input_lambda_enable_logs"></a> [lambda\_enable\_logs](#input\_lambda\_enable\_logs) | Specifies if Lambda should have CloudWatch a dedicated logs group. | `bool` | `false` | no |
46+
| <a name="input_lambda_function_memory_size"></a> [lambda\_function\_memory\_size](#input\_lambda\_function\_memory\_size) | Amount of memory in MB your Lambda Function can use at runtime. Defaults to 1024. | `number` | `1024` | no |
47+
| <a name="input_lambda_function_timeout"></a> [lambda\_function\_timeout](#input\_lambda\_function\_timeout) | Amount of time your Lambda Function has to run in seconds. | `number` | `300` | no |
48+
| <a name="input_lambda_logs_retention_in_days"></a> [lambda\_logs\_retention\_in\_days](#input\_lambda\_logs\_retention\_in\_days) | Specifies the number of days you want to retain log events in the specified log group. | `number` | `30` | no |
49+
| <a name="input_lambda_publish"></a> [lambda\_publish](#input\_lambda\_publish) | Whether to publish creation/change as new Lambda Function Version. | `bool` | `false` | no |
50+
| <a name="input_lambda_source_code_path"></a> [lambda\_source\_code\_path](#input\_lambda\_source\_code\_path) | Path to the lambda source code zip file | `string` | `null` | no |
51+
| <a name="input_resource_name_common_part"></a> [resource\_name\_common\_part](#input\_resource\_name\_common\_part) | A common part for all resources created under the stack | `string` | `null` | no |
52+
| <a name="input_secrets_names"></a> [secrets\_names](#input\_secrets\_names) | Names of secrets to create | `list(string)` | `null` | no |
53+
| <a name="input_store_secret_in_secrets_manager"></a> [store\_secret\_in\_secrets\_manager](#input\_store\_secret\_in\_secrets\_manager) | Whether to store your secrets in secrets manager, default is false | `bool` | `false` | no |
54+
| <a name="input_tags"></a> [tags](#input\_tags) | A collection of tags grouped by key representing it's target resource. | `map(map(string))` | <pre>{<br> "api_gateway": {},<br> "iam": {},<br> "lambda": {}<br>}</pre> | no |
3455

3556
### env_vars
3657

37-
In some integrations, Spectral requires some extra environment variables besides the default ones.
38-
Those extra variables should be added to the `env_vars` map in addition to `SPECTRAL_DSN` which is mandatory.
39-
40-
Please refer to our docs / source pages to view the extra environment variables needed for the integration.
58+
In some integrations, Spectral requires some environment variables besides the default ones.
59+
Those variables should be added to the `env_vars`.
4160

42-
##### SPECTRAL_DSN (mandatory)
61+
Please refer to our [docs](https://guides.spectralops.io/docs/welcome-to-checkpoint-cloudguard-guides) / source pages to view the extra environment variables needed for the integration.
4362

44-
Your SpectralOps identifier, retrieved from your SpectralOps account.
4563

4664
### global_tags
4765

@@ -142,13 +160,20 @@ module "spectral_lambda_integration" {
142160

143161
### This module has the following outputs
144162

145-
1. `rest_api_id` - The ID of the REST API.
146-
2. `rest_api_url` - The URL for accessing the lambda through the ApiGateway.
147-
3. `rest_api_arn` - Amazon Resource Name (ARN) identifying your Rest API.
148-
4. `rest_api_execution_arn` - The execution ARN part to be used in lambda_permission's source_arn, not concatenated to other allowed API resources.
149-
5. `rest_api_lambda_execution_arn` - The execution ARN part to be used in lambda_permission's source_arn, concatenated with allowed API resources (method & path).
150-
6. `lambda_function_arn` - Amazon Resource Name (ARN) identifying your Lambda Function.
151-
7. `lambda_function_name` - The name of the lambda function.
152-
8. `lambda_iam_role_arn` - Amazon Resource Name (ARN) specifying the role.
153-
9. `lambda_iam_role_name` - Name of the role.
154-
10. `secrets_arns` - Arns of created secrets in secrets manager.
163+
| Name | Description |
164+
|------|-------------|
165+
| <a name="output_lambda_function_arn"></a> [lambda\_function\_arn](#output\_lambda\_function\_arn) | Amazon Resource Name (ARN) identifying your Lambda Function |
166+
| <a name="output_lambda_function_name"></a> [lambda\_function\_name](#output\_lambda\_function\_name) | The name of the lambda function |
167+
| <a name="output_lambda_iam_role_arn"></a> [lambda\_iam\_role\_arn](#output\_lambda\_iam\_role\_arn) | Amazon Resource Name (ARN) specifying the role |
168+
| <a name="output_lambda_iam_role_name"></a> [lambda\_iam\_role\_name](#output\_lambda\_iam\_role\_name) | Name of the role |
169+
| <a name="output_rest_api_arn"></a> [rest\_api\_arn](#output\_rest\_api\_arn) | Amazon Resource Name (ARN) identifying your Rest API |
170+
| <a name="output_rest_api_execution_arn"></a> [rest\_api\_execution\_arn](#output\_rest\_api\_execution\_arn) | The execution ARN part to be used in lambda\_permission's source\_arn, not concatenated to other allowed API resources |
171+
| <a name="output_rest_api_id"></a> [rest\_api\_id](#output\_rest\_api\_id) | The ID of the REST API |
172+
| <a name="output_rest_api_lambda_execution_arn"></a> [rest\_api\_lambda\_execution\_arn](#output\_rest\_api\_lambda\_execution\_arn) | The execution ARN part to be used in lambda\_permission's source\_arn, concatenated with allowed API resources (method & path) |
173+
| <a name="output_rest_api_url"></a> [rest\_api\_url](#output\_rest\_api\_url) | The URL for accessing the lambda through the ApiGateway |
174+
| <a name="output_secrets_arns"></a> [secrets\_arns](#output\_secrets\_arns) | Arns of created secrets in secrets manager |
175+
176+
## Support
177+
178+
For GitHub deployment - only bot version 2.x is supported.
179+
The default GitHub bot version that this module deploys is 2.0.4, if you wish to use other versions please set local paths to the relevant ZIP files.

examples/basic-github-integration.tf

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
module "spectral_lambda_integration" {
2+
source = "github.com/SpectralOps/spectral-terraform-lambda-integration"
3+
4+
integration_type = "github"
5+
lambda_enable_logs = true
6+
7+
# Use this attributes to deploy specific version of the bot
8+
frontend_lambda_source_code_path = "./source-code/github/github-frontend.zip"
9+
backend_lambda_source_code_path = "./source-code/github/github-backend.zip"
10+
11+
env_vars = {
12+
# Required environment variables
13+
SPECTRAL_DSN = "MySpectralDSN"
14+
CHECK_POLICY = "Fail on any issue" # (Fail on any issue / Fail on warnings and above / Fail on errors only / Always Pass)
15+
GITHUB_APP_ID = "MyGitHubAppId"
16+
GITHUB_WEBHOOK_SECRET = "MyGitHubWebhookSecret"
17+
GITHUB_PRIVATE_KEY = "MyGitHubPrivateKey"
18+
# Optional environment variables
19+
SECRETS_VAULT = "aws_secrets_manager"
20+
VAULT_KEY_SPECTRAL_DSN = "Spectral_Dsn-..."
21+
VAULT_KEY_GITHUB_WEBHOOK_SECRET = "Spectral_GithubBot_WebhookSecret-..."
22+
VAULT_KEY_GITHUB_PRIVATE_KEY = "Spectral_GithubBot_PrivateKey-..."
23+
GITHUB_SHOULD_POST_REVIEW_COMMENTS = false
24+
GITHUB_SHOULD_SKIP_CHECK = false
25+
S3_BLACK_LIST_OBJECT_KEY = "The S3 object key of your blacklist flie"
26+
S3_BLACK_LIST_BUCKET_NAME = "The S3 bucket name that holds your blacklist file"
27+
SHOULD_SKIP_INGEST = false
28+
STRICT_MODE = false
29+
SPECTRAL_TAGS = "iac,base,audit"
30+
}
31+
}

locals.tf

+15-4
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,17 @@
11
locals {
2-
resource_name_pattern = "spectral-${var.integration_type}-integration-${var.environment}"
3-
single_lambda_integration = contains(["jira", "terraform"], var.integration_type) ? true : false
4-
multiple_lambda_integration = contains(["gitlab"], var.integration_type) ? true : false
5-
api_triggered_function_arn = local.single_lambda_integration ? module.lambda_function[0].lambda_function_arn : module.frontend_lambda_function[0].lambda_function_arn
2+
resource_name_pattern = coalesce(var.resource_name_common_part, "spectral-${var.integration_type}-integration-${var.environment}-${random_string.random_resource_name_suffix.id}")
3+
single_lambda_integration = contains(["jira", "terraform"], var.integration_type) ? true : false
4+
multiple_lambda_integration = contains(["gitlab", "github"], var.integration_type) ? true : false
5+
api_triggered_function_arn = local.single_lambda_integration ? module.lambda_function[0].lambda_function_arn : module.frontend_lambda_function[0].lambda_function_arn
6+
frontend_lambda_handler = contains(["github"], var.integration_type) ? "index.handler" : "frontend.app"
7+
backend_lambda_handler = contains(["github"], var.integration_type) ? "index.handler" : "backend.app"
8+
shared_default_secrets_names = ["Spectral_Dsn"]
9+
default_secrets_names = {
10+
"github" = coalesce(var.secrets_names, concat(local.shared_default_secrets_names, ["Spectral_GithubBot_PrivateKey", "Spectral_GithubBot_WebhookSecret"])),
11+
"gitlab" = coalesce(var.secrets_names, concat(local.shared_default_secrets_names, ["Spectral_GitlabBot_GitlabToken", "Spectral_GitlabBot_WebhookSecret"]))
12+
}
13+
# Please do not change or replace the 'frontend' suffix since there a logic in the bot based in it
14+
api_triggered_function_name = local.single_lambda_integration ? local.resource_name_pattern : "${local.resource_name_pattern}-frontend"
15+
# Merge user env vars with env vars which are not based on user input
16+
env_vars = merge(var.env_vars, { HOME = "/tmp" })
617
}

modules/api_gateway/rest_api.tf

+2-1
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ resource "aws_api_gateway_integration" "api_proxy_integration" {
3131
integration_http_method = "POST"
3232
type = "AWS_PROXY"
3333
uri = var.lambda_function_arn
34+
timeout_milliseconds = var.gateway_api_integration_timeout_milliseconds
3435
}
3536

3637
resource "aws_api_gateway_method_response" "response_200" {
@@ -43,7 +44,7 @@ resource "aws_api_gateway_method_response" "response_200" {
4344
resource "aws_lambda_permission" "lambda_permission" {
4445
statement_id = "AllowExecutionFromAPIGateway"
4546
action = "lambda:InvokeFunction"
46-
function_name = var.resource_name_pattern
47+
function_name = var.function_name
4748
principal = "apigateway.amazonaws.com"
4849
source_arn = local.rest_api_execution_arn
4950
}

modules/api_gateway/variables.tf

+12-1
Original file line numberDiff line numberDiff line change
@@ -33,5 +33,16 @@ variable "tags" {
3333

3434
variable "integration_type" {
3535
type = string
36-
description = "Spectral integration type (A unique phrase describing the integration) - Available values: `terraform`."
36+
description = "Spectral integration type (A unique phrase describing the integration) - Available values: `github`, `terraform`, `jira` and `gitlab`"
37+
}
38+
39+
variable "function_name" {
40+
type = string
41+
description = "The name of the function the API would trigger upon request"
42+
}
43+
44+
variable "gateway_api_integration_timeout_milliseconds" {
45+
description = "Timeout for the API Gateway to wait for lambda response"
46+
type = number
47+
default = 29000
3748
}

modules/lambda/lambda.tf

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
locals {
2-
runtime = "nodejs14.x"
3-
lambda_source_code_zip_path = "${path.module}/source_code/${var.integration_type}/${var.lambda_source_code_filename}"
2+
runtime = "nodejs20.x"
3+
lambda_source_code_zip_path = coalesce(var.lambda_source_code_path, "${path.module}/source_code/${var.integration_type}/${var.lambda_source_code_filename}")
44
}
55

66
resource "aws_lambda_function" "spectral_scanner_lambda" {
8.71 MB
Binary file not shown.
8.72 MB
Binary file not shown.
256 KB
Binary file not shown.
726 KB
Binary file not shown.
0 Bytes
Binary file not shown.
17.7 KB
Binary file not shown.

modules/lambda/variables.tf

+6-6
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ variable "tags" {
4444

4545
variable "integration_type" {
4646
type = string
47-
description = "Spectral integration type (A unique phrase describing the integration) - Available values: `terraform`."
47+
description = "Spectral integration type (A unique phrase describing the integration) - Available values: `github`, `terraform`, `jira` and `gitlab`"
4848
}
4949

5050
variable "timeout" {
@@ -71,16 +71,16 @@ variable "secrets_arns" {
7171
default = []
7272
}
7373

74-
variable "store_secret_in_secrets_manager" {
75-
description = "Whether to store your secrets in secrets manager, default is false"
76-
type = bool
77-
}
78-
7974
variable "lambda_source_code_filename" {
8075
type = string
8176
description = "The lambda source code filename"
8277
}
8378

79+
variable "lambda_source_code_path" {
80+
type = string
81+
description = "The lambda source code path"
82+
}
83+
8484
variable "role_arn" {
8585
type = string
8686
description = "The lambda source code filename"

modules/role/role.tf

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ data "aws_iam_policy_document" "assume_role_policy" {
1313
}
1414

1515
resource "aws_iam_role" "lambda_execution_role" {
16-
name = var.resource_name_pattern
16+
name = var.role_name
1717
assume_role_policy = data.aws_iam_policy_document.assume_role_policy.json
1818

1919
tags = merge(

modules/role/variables.tf

+2-2
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,9 @@ variable "tags" {
2727
}
2828
}
2929

30-
variable "resource_name_pattern" {
30+
variable "role_name" {
3131
type = string
32-
description = "A common resource name created by pattern."
32+
description = "The name of the role"
3333
}
3434

3535
variable "multiple_lambda_integration" {

modules/secrets_manager/gitlab/main.tf

-7
This file was deleted.

modules/secrets_manager/gitlab/outputs.tf

-6
This file was deleted.

modules/secrets_manager/secrets.tf

+4-11
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,8 @@
11
locals {
2-
secrets_arns = concat(
3-
try(module.gitlab[0].secrets_arns, []),
4-
[aws_secretsmanager_secret.spectral_dsn.arn]
5-
)
2+
secrets_arns = [for secret in aws_secretsmanager_secret.general_secret : secret.arn]
63
}
74

8-
resource "aws_secretsmanager_secret" "spectral_dsn" {
9-
name = "Spectral_Dsn"
10-
}
11-
12-
module "gitlab" {
13-
count = var.integration_type == "gitlab" ? 1 : 0
14-
source = "./gitlab"
5+
resource "aws_secretsmanager_secret" "general_secret" {
6+
count = length(var.secrets_names)
7+
name = var.secrets_names[count.index]
158
}

0 commit comments

Comments
 (0)