thinkwork-cli 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/dist/cli.js +251 -47
- package/dist/terraform/examples/greenfield/main.tf +190 -0
- package/dist/terraform/examples/greenfield/terraform.tfvars.example +28 -0
- package/dist/terraform/modules/_internal/workspace-guard/main.tf +29 -0
- package/dist/terraform/modules/app/agentcore-runtime/main.tf +217 -0
- package/dist/terraform/modules/app/appsync-subscriptions/main.tf +122 -0
- package/dist/terraform/modules/app/appsync-subscriptions/outputs.tf +20 -0
- package/dist/terraform/modules/app/appsync-subscriptions/variables.tf +31 -0
- package/dist/terraform/modules/app/crons/main.tf +55 -0
- package/dist/terraform/modules/app/hindsight-memory/README.md +66 -0
- package/dist/terraform/modules/app/hindsight-memory/main.tf +331 -0
- package/dist/terraform/modules/app/job-triggers/main.tf +70 -0
- package/dist/terraform/modules/app/lambda-api/.build/placeholder.zip +0 -0
- package/dist/terraform/modules/app/lambda-api/handlers.tf +311 -0
- package/dist/terraform/modules/app/lambda-api/main.tf +245 -0
- package/dist/terraform/modules/app/lambda-api/outputs.tf +24 -0
- package/dist/terraform/modules/app/lambda-api/variables.tf +153 -0
- package/dist/terraform/modules/app/ses-email/main.tf +51 -0
- package/dist/terraform/modules/app/static-site/main.tf +176 -0
- package/dist/terraform/modules/data/aurora-postgres/README.md +92 -0
- package/dist/terraform/modules/data/aurora-postgres/main.tf +185 -0
- package/dist/terraform/modules/data/aurora-postgres/outputs.tf +30 -0
- package/dist/terraform/modules/data/aurora-postgres/variables.tf +114 -0
- package/dist/terraform/modules/data/bedrock-knowledge-base/main.tf +102 -0
- package/dist/terraform/modules/data/s3-buckets/main.tf +91 -0
- package/dist/terraform/modules/foundation/cognito/main.tf +377 -0
- package/dist/terraform/modules/foundation/cognito/outputs.tf +29 -0
- package/dist/terraform/modules/foundation/cognito/variables.tf +124 -0
- package/dist/terraform/modules/foundation/dns/main.tf +49 -0
- package/dist/terraform/modules/foundation/kms/main.tf +49 -0
- package/dist/terraform/modules/foundation/vpc/main.tf +137 -0
- package/dist/terraform/modules/foundation/vpc/outputs.tf +14 -0
- package/dist/terraform/modules/foundation/vpc/variables.tf +40 -0
- package/dist/terraform/modules/thinkwork/main.tf +212 -0
- package/dist/terraform/modules/thinkwork/outputs.tf +87 -0
- package/dist/terraform/modules/thinkwork/variables.tf +241 -0
- package/dist/terraform/schema.graphql +199 -0
- package/package.json +2 -2
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
variable "stage" {
|
|
2
|
+
description = "Deployment stage"
|
|
3
|
+
type = string
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
variable "account_id" {
|
|
7
|
+
description = "AWS account ID"
|
|
8
|
+
type = string
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
variable "region" {
|
|
12
|
+
description = "AWS region"
|
|
13
|
+
type = string
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
variable "lambda_artifact_bucket" {
|
|
17
|
+
description = "S3 bucket containing Lambda deployment artifacts"
|
|
18
|
+
type = string
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
variable "lambda_artifact_prefix" {
|
|
22
|
+
description = "S3 key prefix for Lambda artifacts (e.g. v0.1.0/lambdas)"
|
|
23
|
+
type = string
|
|
24
|
+
default = "latest/lambdas"
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
# ---------------------------------------------------------------------------
|
|
28
|
+
# Dependencies from other tiers
|
|
29
|
+
# ---------------------------------------------------------------------------
|
|
30
|
+
|
|
31
|
+
variable "db_cluster_arn" {
|
|
32
|
+
description = "Aurora cluster ARN"
|
|
33
|
+
type = string
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
variable "graphql_db_secret_arn" {
|
|
37
|
+
description = "Secrets Manager ARN for DB credentials"
|
|
38
|
+
type = string
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
variable "db_cluster_endpoint" {
|
|
42
|
+
description = "Aurora cluster endpoint (hostname)"
|
|
43
|
+
type = string
|
|
44
|
+
default = ""
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
variable "database_name" {
|
|
48
|
+
description = "Aurora database name"
|
|
49
|
+
type = string
|
|
50
|
+
default = "thinkwork"
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
variable "bucket_name" {
|
|
54
|
+
description = "Primary S3 bucket name"
|
|
55
|
+
type = string
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
variable "bucket_arn" {
|
|
59
|
+
description = "Primary S3 bucket ARN"
|
|
60
|
+
type = string
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
variable "user_pool_id" {
|
|
64
|
+
description = "Cognito user pool ID"
|
|
65
|
+
type = string
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
variable "user_pool_arn" {
|
|
69
|
+
description = "Cognito user pool ARN"
|
|
70
|
+
type = string
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
variable "admin_client_id" {
|
|
74
|
+
description = "Cognito web admin client ID"
|
|
75
|
+
type = string
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
variable "mobile_client_id" {
|
|
79
|
+
description = "Cognito mobile client ID"
|
|
80
|
+
type = string
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
variable "appsync_api_url" {
|
|
84
|
+
description = "AppSync subscriptions endpoint URL"
|
|
85
|
+
type = string
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
variable "appsync_api_key" {
|
|
89
|
+
description = "AppSync API key"
|
|
90
|
+
type = string
|
|
91
|
+
sensitive = true
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
variable "kb_service_role_arn" {
|
|
95
|
+
description = "Bedrock Knowledge Base service role ARN"
|
|
96
|
+
type = string
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
variable "certificate_arn" {
|
|
100
|
+
description = "ACM certificate ARN for custom API domain"
|
|
101
|
+
type = string
|
|
102
|
+
default = ""
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
variable "custom_domain" {
|
|
106
|
+
description = "Custom domain for the API (e.g. api.thinkwork.ai). Leave empty to skip."
|
|
107
|
+
type = string
|
|
108
|
+
default = ""
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
variable "lambda_zips_dir" {
|
|
112
|
+
description = "Local directory containing Lambda zip artifacts (from scripts/build-lambdas.sh). Set to enable real handlers."
|
|
113
|
+
type = string
|
|
114
|
+
default = ""
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
variable "db_password" {
|
|
118
|
+
description = "Database password (used to construct DATABASE_URL for Lambda)"
|
|
119
|
+
type = string
|
|
120
|
+
sensitive = true
|
|
121
|
+
default = ""
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
variable "db_username" {
|
|
125
|
+
description = "Database username"
|
|
126
|
+
type = string
|
|
127
|
+
default = "thinkwork_admin"
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
variable "api_auth_secret" {
|
|
131
|
+
description = "Shared secret for inter-service API authentication"
|
|
132
|
+
type = string
|
|
133
|
+
sensitive = true
|
|
134
|
+
default = ""
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
variable "agentcore_invoke_url" {
|
|
138
|
+
description = "Lambda Function URL for AgentCore container invocation"
|
|
139
|
+
type = string
|
|
140
|
+
default = ""
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
variable "hindsight_endpoint" {
|
|
144
|
+
description = "Hindsight API endpoint (empty when memory_engine = managed)"
|
|
145
|
+
type = string
|
|
146
|
+
default = ""
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
variable "agentcore_function_name" {
|
|
150
|
+
description = "AgentCore Lambda function name (for direct SDK invoke instead of Function URL)"
|
|
151
|
+
type = string
|
|
152
|
+
default = ""
|
|
153
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
################################################################################
|
|
2
|
+
# SES Email — App Module
|
|
3
|
+
#
|
|
4
|
+
# Configures SES for email inbound (receipt rules) and domain verification.
|
|
5
|
+
# Full Lambda wiring comes in Phase 4 when email handlers are migrated.
|
|
6
|
+
# Phase 1 creates the domain identity and DKIM records only.
|
|
7
|
+
################################################################################
|
|
8
|
+
|
|
9
|
+
variable "stage" {
|
|
10
|
+
description = "Deployment stage"
|
|
11
|
+
type = string
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
variable "account_id" {
|
|
15
|
+
description = "AWS account ID"
|
|
16
|
+
type = string
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
variable "email_domain" {
|
|
20
|
+
description = "Domain for SES email (e.g. thinkwork.ai)"
|
|
21
|
+
type = string
|
|
22
|
+
default = ""
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
################################################################################
|
|
26
|
+
# SES Domain Identity (only if email_domain is provided)
|
|
27
|
+
################################################################################
|
|
28
|
+
|
|
29
|
+
resource "aws_ses_domain_identity" "main" {
|
|
30
|
+
count = var.email_domain != "" ? 1 : 0
|
|
31
|
+
domain = var.email_domain
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
resource "aws_ses_domain_dkim" "main" {
|
|
35
|
+
count = var.email_domain != "" ? 1 : 0
|
|
36
|
+
domain = aws_ses_domain_identity.main[0].domain
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
################################################################################
|
|
40
|
+
# Outputs
|
|
41
|
+
################################################################################
|
|
42
|
+
|
|
43
|
+
output "ses_domain_identity_arn" {
|
|
44
|
+
description = "SES domain identity ARN"
|
|
45
|
+
value = var.email_domain != "" ? aws_ses_domain_identity.main[0].arn : null
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
output "dkim_tokens" {
|
|
49
|
+
description = "DKIM tokens for DNS verification"
|
|
50
|
+
value = var.email_domain != "" ? aws_ses_domain_dkim.main[0].dkim_tokens : []
|
|
51
|
+
}
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
################################################################################
|
|
2
|
+
# Static Site — App Module
|
|
3
|
+
#
|
|
4
|
+
# S3 + CloudFront distribution for static web apps (apps/admin, docs site).
|
|
5
|
+
# Reusable for any static frontend that needs a CDN + custom domain.
|
|
6
|
+
################################################################################
|
|
7
|
+
|
|
8
|
+
variable "stage" {
|
|
9
|
+
description = "Deployment stage"
|
|
10
|
+
type = string
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
variable "site_name" {
|
|
14
|
+
description = "Identifier for the site (e.g. admin, docs)"
|
|
15
|
+
type = string
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
variable "bucket_name" {
|
|
19
|
+
description = "S3 bucket name for the site assets"
|
|
20
|
+
type = string
|
|
21
|
+
default = ""
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
variable "custom_domain" {
|
|
25
|
+
description = "Custom domain (e.g. app.thinkwork.ai). Leave empty for CloudFront default."
|
|
26
|
+
type = string
|
|
27
|
+
default = ""
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
variable "certificate_arn" {
|
|
31
|
+
description = "ACM certificate ARN for the custom domain (us-east-1, required for CloudFront)"
|
|
32
|
+
type = string
|
|
33
|
+
default = ""
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
################################################################################
|
|
37
|
+
# S3 Bucket
|
|
38
|
+
################################################################################
|
|
39
|
+
|
|
40
|
+
locals {
|
|
41
|
+
bucket_name = var.bucket_name != "" ? var.bucket_name : "thinkwork-${var.stage}-${var.site_name}"
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
resource "aws_s3_bucket" "site" {
|
|
45
|
+
bucket = local.bucket_name
|
|
46
|
+
|
|
47
|
+
tags = {
|
|
48
|
+
Name = "thinkwork-${var.stage}-${var.site_name}"
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
resource "aws_s3_bucket_public_access_block" "site" {
|
|
53
|
+
bucket = aws_s3_bucket.site.id
|
|
54
|
+
|
|
55
|
+
block_public_acls = true
|
|
56
|
+
block_public_policy = true
|
|
57
|
+
ignore_public_acls = true
|
|
58
|
+
restrict_public_buckets = true
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
################################################################################
|
|
62
|
+
# CloudFront OAC
|
|
63
|
+
################################################################################
|
|
64
|
+
|
|
65
|
+
resource "aws_cloudfront_origin_access_control" "site" {
|
|
66
|
+
name = "thinkwork-${var.stage}-${var.site_name}-oac"
|
|
67
|
+
origin_access_control_origin_type = "s3"
|
|
68
|
+
signing_behavior = "always"
|
|
69
|
+
signing_protocol = "sigv4"
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
################################################################################
|
|
73
|
+
# CloudFront Distribution
|
|
74
|
+
################################################################################
|
|
75
|
+
|
|
76
|
+
resource "aws_cloudfront_distribution" "site" {
|
|
77
|
+
enabled = true
|
|
78
|
+
default_root_object = "index.html"
|
|
79
|
+
aliases = var.custom_domain != "" ? [var.custom_domain] : []
|
|
80
|
+
price_class = "PriceClass_100"
|
|
81
|
+
|
|
82
|
+
origin {
|
|
83
|
+
domain_name = aws_s3_bucket.site.bucket_regional_domain_name
|
|
84
|
+
origin_id = "s3-${local.bucket_name}"
|
|
85
|
+
origin_access_control_id = aws_cloudfront_origin_access_control.site.id
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
default_cache_behavior {
|
|
89
|
+
target_origin_id = "s3-${local.bucket_name}"
|
|
90
|
+
viewer_protocol_policy = "redirect-to-https"
|
|
91
|
+
allowed_methods = ["GET", "HEAD", "OPTIONS"]
|
|
92
|
+
cached_methods = ["GET", "HEAD"]
|
|
93
|
+
compress = true
|
|
94
|
+
|
|
95
|
+
forwarded_values {
|
|
96
|
+
query_string = false
|
|
97
|
+
cookies {
|
|
98
|
+
forward = "none"
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
custom_error_response {
|
|
104
|
+
error_code = 404
|
|
105
|
+
response_code = 200
|
|
106
|
+
response_page_path = "/index.html"
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
custom_error_response {
|
|
110
|
+
error_code = 403
|
|
111
|
+
response_code = 200
|
|
112
|
+
response_page_path = "/index.html"
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
restrictions {
|
|
116
|
+
geo_restriction {
|
|
117
|
+
restriction_type = "none"
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
viewer_certificate {
|
|
122
|
+
cloudfront_default_certificate = var.certificate_arn == ""
|
|
123
|
+
acm_certificate_arn = var.certificate_arn != "" ? var.certificate_arn : null
|
|
124
|
+
ssl_support_method = var.certificate_arn != "" ? "sni-only" : null
|
|
125
|
+
minimum_protocol_version = var.certificate_arn != "" ? "TLSv1.2_2021" : null
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
tags = {
|
|
129
|
+
Name = "thinkwork-${var.stage}-${var.site_name}"
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
################################################################################
|
|
134
|
+
# S3 Bucket Policy — CloudFront access
|
|
135
|
+
################################################################################
|
|
136
|
+
|
|
137
|
+
resource "aws_s3_bucket_policy" "site" {
|
|
138
|
+
bucket = aws_s3_bucket.site.id
|
|
139
|
+
|
|
140
|
+
policy = jsonencode({
|
|
141
|
+
Version = "2012-10-17"
|
|
142
|
+
Statement = [{
|
|
143
|
+
Sid = "AllowCloudFrontOAC"
|
|
144
|
+
Effect = "Allow"
|
|
145
|
+
Principal = {
|
|
146
|
+
Service = "cloudfront.amazonaws.com"
|
|
147
|
+
}
|
|
148
|
+
Action = "s3:GetObject"
|
|
149
|
+
Resource = "${aws_s3_bucket.site.arn}/*"
|
|
150
|
+
Condition = {
|
|
151
|
+
StringEquals = {
|
|
152
|
+
"AWS:SourceArn" = aws_cloudfront_distribution.site.arn
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}]
|
|
156
|
+
})
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
################################################################################
|
|
160
|
+
# Outputs
|
|
161
|
+
################################################################################
|
|
162
|
+
|
|
163
|
+
output "distribution_id" {
|
|
164
|
+
description = "CloudFront distribution ID (for cache invalidation)"
|
|
165
|
+
value = aws_cloudfront_distribution.site.id
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
output "distribution_domain" {
|
|
169
|
+
description = "CloudFront distribution domain name"
|
|
170
|
+
value = aws_cloudfront_distribution.site.domain_name
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
output "bucket_name" {
|
|
174
|
+
description = "S3 bucket name for the site"
|
|
175
|
+
value = aws_s3_bucket.site.id
|
|
176
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# PostgreSQL Database Module
|
|
2
|
+
|
|
3
|
+
Creates a PostgreSQL database for Thinkwork. Supports two engines selectable by stage — use cheaper RDS for dev/test and Aurora Serverless for production.
|
|
4
|
+
|
|
5
|
+
## Engines
|
|
6
|
+
|
|
7
|
+
| Engine | `database_engine` value | Best for | Deletion protection | Typical deploy time |
|
|
8
|
+
|--------|------------------------|----------|--------------------|--------------------|
|
|
9
|
+
| **Aurora Serverless v2** | `aurora-serverless` | Production. Auto-scales 0.5–2 ACU by default. | ON by default | ~6 min |
|
|
10
|
+
| **Standard RDS PostgreSQL** | `rds-postgres` | Dev/test. Single `db.t4g.micro` instance, 20 GB. | OFF by default | ~5 min |
|
|
11
|
+
|
|
12
|
+
Both engines share the same output interface — downstream modules don't need to know which is running.
|
|
13
|
+
|
|
14
|
+
## Usage
|
|
15
|
+
|
|
16
|
+
### Aurora (production)
|
|
17
|
+
|
|
18
|
+
```hcl
|
|
19
|
+
module "database" {
|
|
20
|
+
source = "thinkwork-ai/thinkwork/aws//modules/data/aurora-postgres"
|
|
21
|
+
|
|
22
|
+
stage = "prod"
|
|
23
|
+
vpc_id = module.vpc.vpc_id
|
|
24
|
+
subnet_ids = module.vpc.public_subnet_ids
|
|
25
|
+
db_password = var.db_password
|
|
26
|
+
|
|
27
|
+
database_engine = "aurora-serverless"
|
|
28
|
+
min_capacity = 0.5
|
|
29
|
+
max_capacity = 4
|
|
30
|
+
}
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
### RDS (dev/test)
|
|
34
|
+
|
|
35
|
+
```hcl
|
|
36
|
+
module "database" {
|
|
37
|
+
source = "thinkwork-ai/thinkwork/aws//modules/data/aurora-postgres"
|
|
38
|
+
|
|
39
|
+
stage = "dev"
|
|
40
|
+
vpc_id = module.vpc.vpc_id
|
|
41
|
+
subnet_ids = module.vpc.public_subnet_ids
|
|
42
|
+
db_password = var.db_password
|
|
43
|
+
|
|
44
|
+
database_engine = "rds-postgres"
|
|
45
|
+
rds_instance_class = "db.t4g.micro"
|
|
46
|
+
}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### BYO (existing database)
|
|
50
|
+
|
|
51
|
+
```hcl
|
|
52
|
+
module "database" {
|
|
53
|
+
source = "thinkwork-ai/thinkwork/aws//modules/data/aurora-postgres"
|
|
54
|
+
|
|
55
|
+
stage = "prod"
|
|
56
|
+
create_database = false
|
|
57
|
+
|
|
58
|
+
existing_db_cluster_arn = "arn:aws:rds:us-east-1:123456789012:cluster:my-cluster"
|
|
59
|
+
existing_db_secret_arn = "arn:aws:secretsmanager:us-east-1:123456789012:secret:my-db-creds"
|
|
60
|
+
existing_db_endpoint = "my-cluster.cluster-abc123.us-east-1.rds.amazonaws.com"
|
|
61
|
+
existing_db_security_group_id = "sg-0123456789abcdef0"
|
|
62
|
+
}
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## Variables
|
|
66
|
+
|
|
67
|
+
| Name | Type | Default | Description |
|
|
68
|
+
|------|------|---------|-------------|
|
|
69
|
+
| `stage` | string | — | Deployment stage (required) |
|
|
70
|
+
| `database_engine` | string | `"aurora-serverless"` | `aurora-serverless` or `rds-postgres` |
|
|
71
|
+
| `create_database` | bool | `true` | Set `false` for BYO |
|
|
72
|
+
| `vpc_id` | string | `""` | VPC ID (required when creating) |
|
|
73
|
+
| `subnet_ids` | list(string) | `[]` | Subnet IDs (required when creating) |
|
|
74
|
+
| `db_password` | string | `""` | Master password (required when creating) |
|
|
75
|
+
| `database_name` | string | `"thinkwork"` | Database name |
|
|
76
|
+
| `engine_version` | string | `"15.10"` | PostgreSQL version |
|
|
77
|
+
| `min_capacity` | number | `0.5` | Aurora min ACU (aurora-serverless only) |
|
|
78
|
+
| `max_capacity` | number | `2` | Aurora max ACU (aurora-serverless only) |
|
|
79
|
+
| `rds_instance_class` | string | `"db.t4g.micro"` | RDS instance class (rds-postgres only) |
|
|
80
|
+
| `rds_allocated_storage` | number | `20` | Storage in GB (rds-postgres only) |
|
|
81
|
+
| `deletion_protection` | bool | `null` | Auto: `true` for Aurora, `false` for RDS |
|
|
82
|
+
|
|
83
|
+
## Outputs
|
|
84
|
+
|
|
85
|
+
| Name | Description |
|
|
86
|
+
|------|-------------|
|
|
87
|
+
| `cluster_endpoint` | Database hostname |
|
|
88
|
+
| `db_cluster_arn` | Database ARN |
|
|
89
|
+
| `graphql_db_secret_arn` | Secrets Manager ARN for credentials |
|
|
90
|
+
| `db_security_group_id` | Security group ID |
|
|
91
|
+
| `database_url` | Full connection string (sensitive) |
|
|
92
|
+
| `database_engine` | Which engine is running |
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
################################################################################
|
|
2
|
+
# PostgreSQL Database — Data Module
|
|
3
|
+
#
|
|
4
|
+
# Supports two engines:
|
|
5
|
+
# - aurora-serverless: Aurora Serverless v2 (production, auto-scaling)
|
|
6
|
+
# - rds-postgres: Standard RDS PostgreSQL (dev/test, cheaper, single instance)
|
|
7
|
+
#
|
|
8
|
+
# Both share the same output interface so downstream modules don't care
|
|
9
|
+
# which engine is running. BYO support via create_database = false.
|
|
10
|
+
################################################################################
|
|
11
|
+
|
|
12
|
+
locals {
|
|
13
|
+
create = var.create_database
|
|
14
|
+
use_aurora = local.create && var.database_engine == "aurora-serverless"
|
|
15
|
+
use_rds = local.create && var.database_engine == "rds-postgres"
|
|
16
|
+
|
|
17
|
+
cluster_identifier = "thinkwork-${var.stage}-db"
|
|
18
|
+
master_username = "thinkwork_admin"
|
|
19
|
+
|
|
20
|
+
# Deletion protection: default to true for Aurora, false for RDS
|
|
21
|
+
deletion_protection = var.deletion_protection != null ? var.deletion_protection : (var.database_engine == "aurora-serverless")
|
|
22
|
+
|
|
23
|
+
# Unified outputs regardless of engine
|
|
24
|
+
db_cluster_arn = local.use_aurora ? aws_rds_cluster.main[0].arn : (
|
|
25
|
+
local.use_rds ? aws_db_instance.main[0].arn : var.existing_db_cluster_arn
|
|
26
|
+
)
|
|
27
|
+
graphql_db_secret_arn = local.create ? aws_secretsmanager_secret.db_credentials[0].arn : var.existing_db_secret_arn
|
|
28
|
+
cluster_endpoint = local.use_aurora ? aws_rds_cluster.main[0].endpoint : (
|
|
29
|
+
local.use_rds ? aws_db_instance.main[0].address : var.existing_db_endpoint
|
|
30
|
+
)
|
|
31
|
+
db_security_group_id = local.create ? aws_security_group.db[0].id : var.existing_db_security_group_id
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
data "aws_region" "current" {}
|
|
35
|
+
data "aws_caller_identity" "current" {}
|
|
36
|
+
|
|
37
|
+
################################################################################
|
|
38
|
+
# Security Group (shared by both engines)
|
|
39
|
+
################################################################################
|
|
40
|
+
|
|
41
|
+
resource "aws_security_group" "db" {
|
|
42
|
+
count = local.create ? 1 : 0
|
|
43
|
+
description = "Security group for Thinkwork PostgreSQL database"
|
|
44
|
+
vpc_id = var.vpc_id
|
|
45
|
+
|
|
46
|
+
ingress {
|
|
47
|
+
from_port = 5432
|
|
48
|
+
to_port = 5432
|
|
49
|
+
protocol = "tcp"
|
|
50
|
+
cidr_blocks = ["0.0.0.0/0"]
|
|
51
|
+
description = "PostgreSQL access"
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
egress {
|
|
55
|
+
from_port = 0
|
|
56
|
+
to_port = 0
|
|
57
|
+
protocol = "-1"
|
|
58
|
+
cidr_blocks = ["0.0.0.0/0"]
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
tags = {
|
|
62
|
+
Name = "thinkwork-${var.stage}-db-sg"
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
lifecycle {
|
|
66
|
+
ignore_changes = [name]
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
################################################################################
|
|
71
|
+
# DB Subnet Group (shared by both engines)
|
|
72
|
+
################################################################################
|
|
73
|
+
|
|
74
|
+
resource "aws_db_subnet_group" "main" {
|
|
75
|
+
count = local.create ? 1 : 0
|
|
76
|
+
name = "thinkwork-${var.stage}-db-subnet-group"
|
|
77
|
+
subnet_ids = var.subnet_ids
|
|
78
|
+
|
|
79
|
+
tags = {
|
|
80
|
+
Name = "thinkwork-${var.stage}-db-subnet-group"
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
################################################################################
|
|
85
|
+
# Aurora Serverless v2 (production engine)
|
|
86
|
+
################################################################################
|
|
87
|
+
|
|
88
|
+
resource "aws_rds_cluster" "main" {
|
|
89
|
+
count = local.use_aurora ? 1 : 0
|
|
90
|
+
|
|
91
|
+
cluster_identifier = local.cluster_identifier
|
|
92
|
+
engine = "aurora-postgresql"
|
|
93
|
+
engine_version = var.engine_version
|
|
94
|
+
database_name = var.database_name
|
|
95
|
+
master_username = local.master_username
|
|
96
|
+
master_password = var.db_password
|
|
97
|
+
|
|
98
|
+
db_subnet_group_name = aws_db_subnet_group.main[0].name
|
|
99
|
+
vpc_security_group_ids = [aws_security_group.db[0].id]
|
|
100
|
+
|
|
101
|
+
enable_http_endpoint = true
|
|
102
|
+
skip_final_snapshot = true
|
|
103
|
+
deletion_protection = local.deletion_protection
|
|
104
|
+
storage_encrypted = true
|
|
105
|
+
|
|
106
|
+
serverlessv2_scaling_configuration {
|
|
107
|
+
min_capacity = var.min_capacity
|
|
108
|
+
max_capacity = var.max_capacity
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
tags = {
|
|
112
|
+
Name = "thinkwork-${var.stage}-db"
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
resource "aws_rds_cluster_instance" "main" {
|
|
117
|
+
count = local.use_aurora ? 1 : 0
|
|
118
|
+
|
|
119
|
+
identifier = "thinkwork-${var.stage}-db-1"
|
|
120
|
+
cluster_identifier = aws_rds_cluster.main[0].id
|
|
121
|
+
instance_class = "db.serverless"
|
|
122
|
+
engine = aws_rds_cluster.main[0].engine
|
|
123
|
+
engine_version = aws_rds_cluster.main[0].engine_version
|
|
124
|
+
|
|
125
|
+
publicly_accessible = true
|
|
126
|
+
db_subnet_group_name = aws_db_subnet_group.main[0].name
|
|
127
|
+
|
|
128
|
+
tags = {
|
|
129
|
+
Name = "thinkwork-${var.stage}-db-1"
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
################################################################################
|
|
134
|
+
# Standard RDS PostgreSQL (dev/test engine)
|
|
135
|
+
################################################################################
|
|
136
|
+
|
|
137
|
+
resource "aws_db_instance" "main" {
|
|
138
|
+
count = local.use_rds ? 1 : 0
|
|
139
|
+
|
|
140
|
+
identifier = local.cluster_identifier
|
|
141
|
+
engine = "postgres"
|
|
142
|
+
engine_version = var.engine_version
|
|
143
|
+
instance_class = var.rds_instance_class
|
|
144
|
+
db_name = var.database_name
|
|
145
|
+
username = local.master_username
|
|
146
|
+
password = var.db_password
|
|
147
|
+
|
|
148
|
+
allocated_storage = var.rds_allocated_storage
|
|
149
|
+
max_allocated_storage = var.rds_allocated_storage * 2
|
|
150
|
+
storage_encrypted = true
|
|
151
|
+
|
|
152
|
+
db_subnet_group_name = aws_db_subnet_group.main[0].name
|
|
153
|
+
vpc_security_group_ids = [aws_security_group.db[0].id]
|
|
154
|
+
|
|
155
|
+
publicly_accessible = true
|
|
156
|
+
skip_final_snapshot = true
|
|
157
|
+
deletion_protection = local.deletion_protection
|
|
158
|
+
|
|
159
|
+
tags = {
|
|
160
|
+
Name = "thinkwork-${var.stage}-db"
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
################################################################################
|
|
165
|
+
# Secrets Manager — DB Credentials (shared by both engines)
|
|
166
|
+
################################################################################
|
|
167
|
+
|
|
168
|
+
resource "aws_secretsmanager_secret" "db_credentials" {
|
|
169
|
+
count = local.create ? 1 : 0
|
|
170
|
+
name = "thinkwork-${var.stage}-db-credentials"
|
|
171
|
+
|
|
172
|
+
tags = {
|
|
173
|
+
Name = "thinkwork-${var.stage}-db-credentials"
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
resource "aws_secretsmanager_secret_version" "db_credentials" {
|
|
178
|
+
count = local.create ? 1 : 0
|
|
179
|
+
secret_id = aws_secretsmanager_secret.db_credentials[0].id
|
|
180
|
+
|
|
181
|
+
secret_string = jsonencode({
|
|
182
|
+
username = local.master_username
|
|
183
|
+
password = var.db_password
|
|
184
|
+
})
|
|
185
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
output "cluster_endpoint" {
|
|
2
|
+
description = "Database endpoint (created or existing)"
|
|
3
|
+
value = local.cluster_endpoint
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
output "db_cluster_arn" {
|
|
7
|
+
description = "Database ARN (created or existing)"
|
|
8
|
+
value = local.db_cluster_arn
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
output "graphql_db_secret_arn" {
|
|
12
|
+
description = "Secrets Manager ARN for DB credentials (created or existing)"
|
|
13
|
+
value = local.graphql_db_secret_arn
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
output "db_security_group_id" {
|
|
17
|
+
description = "Security group ID for the database (created or existing)"
|
|
18
|
+
value = local.db_security_group_id
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
output "database_url" {
|
|
22
|
+
description = "PostgreSQL connection string (only available when create_database = true)"
|
|
23
|
+
value = local.create ? "postgresql://${local.master_username}:${var.db_password}@${local.cluster_endpoint}:5432/${var.database_name}" : null
|
|
24
|
+
sensitive = true
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
output "database_engine" {
|
|
28
|
+
description = "Which engine is running (aurora-serverless or rds-postgres)"
|
|
29
|
+
value = var.database_engine
|
|
30
|
+
}
|