Skip to content

Commit 817fea1

Browse files
committed
chore: initial commit
0 parents  commit 817fea1

File tree

112 files changed

+20153
-0
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

112 files changed

+20153
-0
lines changed

.github/CODEOWNERS

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
* @skyleague/oss
2+
package-lock.json

.github/workflows/tfsec.yml

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
name: tfsec
2+
on: push
3+
4+
jobs:
5+
tfsec:
6+
uses: skyleague/node-standards/.github/workflows/reusable-tfsec.yml@main
7+
secrets:
8+
GITHUB_APP_ID: ${{ secrets.STARCHART_MODULES_APP_ID }}
9+
GITHUB_APP_PEM: ${{ secrets.STARCHART_MODULES_APP_PEM }}
10+
with:
11+
terraform-version: '1.4.6'
12+
working-directory: './'

.gitignore

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
.*
2+
__pycache__
3+
node_modules
4+
5+
!.github
6+
!.vscode
7+
!.npmrc
8+
!.husky
9+
10+
!.gitignore
11+
!.gitkeep

.husky/commit-msg

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
npx --no -- commitlint --edit $1

.husky/pre-commit

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
npx --no -- lint-staged --concurrent=true --allow-empty

.npmrc

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
//registry.npmjs.org/:_authToken=${NPM_TOKEN}

.vscode/extensions.json

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"recommendations": ["biomejs.biome"],
3+
"unwantedRecommendations": ["esbenp.prettier-vscode", "dbaeumer.vscode-eslint"]
4+
}

.vscode/settings.json

+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
{
2+
"debug.javascript.terminalOptions": {
3+
"remoteRoot": null,
4+
"skipFiles": ["<node_internals>/**"],
5+
"sourceMaps": true
6+
},
7+
"editor.codeActionsOnSave": {
8+
"source.organizeImports.biome": "explicit",
9+
"quickfix.biome": "explicit"
10+
},
11+
"editor.defaultFormatter": "biomejs.biome",
12+
"editor.formatOnSave": true,
13+
"editor.wordSeparators": "`~!@#%^&*()-=+[{]}\\|;:'\",.<>/?",
14+
"files.eol": "\n",
15+
"files.exclude": {
16+
"**/.coverage": true,
17+
"**/.dist": true,
18+
"**/.DS_Store": true,
19+
"**/.git": true,
20+
"**/.hg": true,
21+
"**/.husky": true,
22+
"**/.svn": true,
23+
"**/CVS": true,
24+
"**/node_modules": true
25+
},
26+
"files.watcherExclude": {
27+
"**/.coverage/*/**": true,
28+
"**/.dist/**": true,
29+
"**/.git/objects/**": true,
30+
"**/.git/subtree-cache/**": true,
31+
"**/node_modules/*/**": true,
32+
"src/**/*.d.ts": true
33+
},
34+
"typescript.implementationsCodeLens.enabled": true,
35+
"typescript.preferences.autoImportFileExcludePatterns": ["**/index.ts"],
36+
"typescript.referencesCodeLens.enabled": true,
37+
"typescript.tsdk": "./node_modules/typescript/lib",
38+
"typescript.enablePromptUseWorkspaceTsdk": true
39+
}

LICENSE.md

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
Copyright (c) 2024, SkyLeague Technologies B.V.. 'SkyLeague' and the astronaut logo are trademarks of SkyLeague Technologies, registered at Chamber of Commerce in The Netherlands under number 86650564.
2+
3+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4+
5+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6+
7+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

README.md

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
# SkyLeague StarChart: Streamlined Serverless Deployment on AWS
2+
3+
StarChart is a collection of Terraform modules designed to simplify the development of serverless microservices using AWS Lambda and other AWS services, such as SQS, EventBridge, DynamoDB, S3, and more.
4+
5+
## Streamlined Function Deployment
6+
7+
With StarChart, managing multiple Lambda functions in a single repository becomes easy. Simply create `handler.yml` files alongside your handler definitions (e.g., `index.ts`). Specify the location of the `handler.yml` files, and StarChart handles the rest! The basic function definition resembles the Serverless framework, but with added opinionated syntax to accelerate development. It adheres to the Principle of Least Privilege, eliminating the need to manually create a policy for each Lambda Function you deploy.
8+
9+
## Deployment Options: Managed or Modular
10+
11+
StarChart offers two ways to deploy your microservice:
12+
13+
1. **Fully-Managed Module**: Deploy your microservice based on the `handler.yml` files next to your functions.
14+
2. **Modular Components**: Customize your deployment by selecting from several modular components that StarChart defines.
15+
16+
## Effortless Deployment with Low Configuration
17+
18+
Deploying a new Lambda Function with StarChart is seamless. Apart from the initial setup, you rarely need to modify the Terraform code. StarChart requires minimal configuration for basic deployments but allows additional customization if necessary. For example, you can configure extra IAM policies for Lambda Functions, add environment variables, or adjust settings for other resources such as SQS queues and API Gateway properties. StarChart's flexible and modular design lets you adapt it to suit your specific needs.
19+
20+
## Deployment Best Practices
21+
22+
### Separate Runtime and Persistent Components
23+
24+
While StarChart handles deploying the runtime components of your application, such as AWS Lambda and SQS, it is recommended to define the persistent deployment of your stack as a separate Terraform deployment. The persistent part of the stack refers to anything that holds data or plays a crucial role in your AWS account's infrastructure.
25+
26+
### Two-Stage Microservice Deployment
27+
28+
For example, a central repository could deploy EventBridge and an S3 bucket that holds Lambda Function artifacts, along with centrally managed resources such as an API Gateway logging role. Then, several microservice repositories could deploy in two stages: a `persistent` stage and a `runtime` stage. In the `persistent` stage, define resources like S3 or DynamoDB, Textract, or Secrets Manager Secret placeholders. In the `runtime` stage, deploy all your Lambda Functions, SQS and EventBridge rules that connect them, an API Gateway with Lambda behind it, etc., – anything that can be safely discarded.
29+
30+
## Example Reference Project
31+
32+
To better understand how to use StarChart, refer to the [`skyleague-standards`](https://github.com/skyleague-internal/standards) project. This project provides a complete example of deploying a Lambda Function with TypeScript using StarChart, and demonstrates how to use the StarChart module as a starter template.

biome.json

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"extends": ["@skyleague/node-standards/biome"]
3+
}

bootstrap/apigateway.tf

+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
variable "attach_api_gateway_cloudwatch_role" {
2+
type = bool
3+
default = true
4+
nullable = false
5+
description = "Attach the API Gateway CloudWatch role to the account, only needs to happen once per account"
6+
}
7+
8+
data "aws_iam_policy_document" "apigateway_cloudwatch_role_assume_role" {
9+
count = var.attach_api_gateway_cloudwatch_role ? 1 : 0
10+
11+
statement {
12+
effect = "Allow"
13+
actions = ["sts:AssumeRole"]
14+
principals {
15+
type = "Service"
16+
identifiers = ["apigateway.amazonaws.com"]
17+
}
18+
}
19+
}
20+
21+
resource "aws_iam_role" "apigateway_cloudwatch_role" {
22+
count = var.attach_api_gateway_cloudwatch_role ? 1 : 0
23+
24+
name_prefix = "api-gateway-cloudwatch-global"
25+
assume_role_policy = data.aws_iam_policy_document.apigateway_cloudwatch_role_assume_role[0].json
26+
}
27+
28+
resource "aws_iam_role_policy_attachment" "logging" {
29+
count = var.attach_api_gateway_cloudwatch_role ? 1 : 0
30+
31+
role = aws_iam_role.apigateway_cloudwatch_role[0].id
32+
policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonAPIGatewayPushToCloudWatchLogs"
33+
}
34+
35+
resource "aws_api_gateway_account" "apigateway_account" {
36+
count = var.attach_api_gateway_cloudwatch_role ? 1 : 0
37+
38+
cloudwatch_role_arn = aws_iam_role.apigateway_cloudwatch_role[0].arn
39+
40+
lifecycle {
41+
ignore_changes = [cloudwatch_role_arn]
42+
}
43+
}

bootstrap/appconfig.tf

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
resource "aws_appconfig_application" "application" {
2+
name = local.config.project_name
3+
description = "Configuration application for ${local.config.project_name}"
4+
}
5+
6+
output "appconfig_application_id" {
7+
value = aws_appconfig_application.application.id
8+
}
9+
10+
output "appconfig_application_arn" {
11+
value = aws_appconfig_application.application.arn
12+
}

bootstrap/artifacts.tf

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
module "artifacts_bucket" {
2+
source = "../modules/aws-s3"
3+
4+
bucket_name_prefix = "${local.config.project_identifier}-artifacts"
5+
}
6+
7+
output "artifacts_bucket_id" {
8+
value = module.artifacts_bucket.this.id
9+
}

bootstrap/config.tf

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
module "config" {
2+
source = "../modules/_config"
3+
4+
project_name = var.starchart.config.project_name
5+
project_identifier = var.starchart.config.project_identifier
6+
environment = var.starchart.config.environment
7+
stack = var.starchart.config.stack
8+
domain = var.starchart.config.domain
9+
repo_root = var.starchart.config.repo_root
10+
}
11+
12+
locals {
13+
config = module.config
14+
}

bootstrap/group.tf

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
resource "aws_resourcegroups_group" "application" {
2+
name = module.config.project_name
3+
tags = {
4+
Name = ""
5+
}
6+
7+
resource_query {
8+
query = jsonencode({
9+
ResourceTypeFilters = ["AWS::AllSupported"],
10+
TagFilters = [
11+
{
12+
Key = "Name"
13+
Values = [module.config.project_name]
14+
},
15+
]
16+
})
17+
}
18+
}

bootstrap/kms.tf

+96
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
2+
variable "eventing_kms_key_arn" {
3+
type = string
4+
nullable = false
5+
default = ""
6+
description = "The ID of the KMS key to use for encrypting the runtime events."
7+
}
8+
9+
data "aws_iam_policy_document" "eventing_kms_key" {
10+
count = length(var.eventing_kms_key_arn) == 0 ? 1 : 0
11+
statement {
12+
sid = "Enable IAM User Permissions"
13+
effect = "Allow"
14+
actions = ["kms:*"]
15+
resources = ["*"]
16+
17+
principals {
18+
type = "AWS"
19+
identifiers = ["arn:aws:iam::${var.starchart.aws_account_id}:root"]
20+
}
21+
}
22+
23+
statement {
24+
sid = "Allow use of the key"
25+
effect = "Allow"
26+
actions = [
27+
"kms:Encrypt",
28+
"kms:Decrypt",
29+
"kms:ReEncrypt*",
30+
"kms:GenerateDataKey*",
31+
"kms:DescribeKey"
32+
]
33+
resources = ["*"]
34+
35+
principals {
36+
type = "Service"
37+
identifiers = [
38+
"events.amazonaws.com",
39+
"sqs.amazonaws.com",
40+
"sns.amazonaws.com"
41+
]
42+
}
43+
44+
condition {
45+
test = "StringEquals"
46+
variable = "aws:SourceAccount"
47+
values = [var.starchart.aws_account_id]
48+
}
49+
}
50+
51+
statement {
52+
sid = "Allow attachment of persistent resources"
53+
effect = "Allow"
54+
actions = [
55+
"kms:CreateGrant",
56+
"kms:ListGrants",
57+
"kms:RevokeGrant"
58+
]
59+
resources = ["*"]
60+
61+
principals {
62+
type = "Service"
63+
identifiers = [
64+
"events.amazonaws.com",
65+
"sqs.amazonaws.com",
66+
"sns.amazonaws.com"
67+
]
68+
}
69+
70+
condition {
71+
test = "Bool"
72+
variable = "kms:GrantIsForAWSResource"
73+
values = ["true"]
74+
}
75+
}
76+
}
77+
78+
resource "aws_kms_key" "eventing_kms_key" {
79+
count = length(var.eventing_kms_key_arn) == 0 ? 1 : 0
80+
81+
description = "KMS key for encrypting all runtime eventing data"
82+
deletion_window_in_days = 30
83+
enable_key_rotation = true
84+
policy = data.aws_iam_policy_document.eventing_kms_key[0].json
85+
}
86+
87+
resource "aws_kms_alias" "eventing_kms_key" {
88+
count = length(var.eventing_kms_key_arn) == 0 ? 1 : 0
89+
90+
name_prefix = "alias/${local.config.project_name}/eventing"
91+
target_key_id = aws_kms_key.eventing_kms_key[0].key_id
92+
}
93+
94+
output "eventing_kms_key_arn" {
95+
value = length(var.eventing_kms_key_arn) == 0 ? aws_kms_key.eventing_kms_key[0].arn : var.eventing_kms_key_arn
96+
}

bootstrap/variables.tf

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
variable "starchart" {
2+
type = object({
3+
aws_account_id = string
4+
config = any
5+
})
6+
}

commitlint.config.ts

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export default { extends: ['@commitlint/config-conventional'] }

lint-staged.config.cjs

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
module.exports = {
2+
'*': ['biome check --no-errors-on-unmatched --files-ignore-unknown=true --write --unsafe'],
3+
}

modules/_common/config.tf

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
variable "config" {
2+
type = object({
3+
project_name = string
4+
project_identifier = string
5+
environment = string
6+
stack = string
7+
domain = string
8+
repo_root = string
9+
})
10+
description = "The configuration for the project"
11+
nullable = false
12+
}
13+
14+
module "config" {
15+
source = "../_config"
16+
17+
project_name = var.config.project_name
18+
project_identifier = var.config.project_identifier
19+
environment = var.config.environment
20+
stack = var.config.stack
21+
domain = var.config.domain
22+
repo_root = var.config.repo_root
23+
}
24+
25+
output "config" {
26+
value = module.config
27+
}

modules/_common/group.tf

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
resource "aws_resourcegroups_group" "stack" {
2+
count = var.default_tags["StackType"] != null ? 1 : 0
3+
name = "${var.config.stack}-${lower(var.default_tags["StackType"])}"
4+
tags = {
5+
Name = var.config.project_name
6+
Stack = ""
7+
}
8+
9+
resource_query {
10+
query = jsonencode({
11+
ResourceTypeFilters = ["AWS::AllSupported"],
12+
TagFilters = [
13+
{
14+
Key = "Name"
15+
Values = [var.config.project_name]
16+
},
17+
{
18+
Key = "Stack"
19+
Values = [var.config.stack]
20+
},
21+
{
22+
Key = "StackType"
23+
Values = [var.default_tags["StackType"]]
24+
}
25+
]
26+
})
27+
}
28+
}

modules/_common/variables.tf

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
variable "default_tags" {
2+
type = map(string)
3+
default = {}
4+
}

0 commit comments

Comments
 (0)