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.
Files changed (39) hide show
  1. package/README.md +3 -3
  2. package/dist/cli.js +251 -47
  3. package/dist/terraform/examples/greenfield/main.tf +190 -0
  4. package/dist/terraform/examples/greenfield/terraform.tfvars.example +28 -0
  5. package/dist/terraform/modules/_internal/workspace-guard/main.tf +29 -0
  6. package/dist/terraform/modules/app/agentcore-runtime/main.tf +217 -0
  7. package/dist/terraform/modules/app/appsync-subscriptions/main.tf +122 -0
  8. package/dist/terraform/modules/app/appsync-subscriptions/outputs.tf +20 -0
  9. package/dist/terraform/modules/app/appsync-subscriptions/variables.tf +31 -0
  10. package/dist/terraform/modules/app/crons/main.tf +55 -0
  11. package/dist/terraform/modules/app/hindsight-memory/README.md +66 -0
  12. package/dist/terraform/modules/app/hindsight-memory/main.tf +331 -0
  13. package/dist/terraform/modules/app/job-triggers/main.tf +70 -0
  14. package/dist/terraform/modules/app/lambda-api/.build/placeholder.zip +0 -0
  15. package/dist/terraform/modules/app/lambda-api/handlers.tf +311 -0
  16. package/dist/terraform/modules/app/lambda-api/main.tf +245 -0
  17. package/dist/terraform/modules/app/lambda-api/outputs.tf +24 -0
  18. package/dist/terraform/modules/app/lambda-api/variables.tf +153 -0
  19. package/dist/terraform/modules/app/ses-email/main.tf +51 -0
  20. package/dist/terraform/modules/app/static-site/main.tf +176 -0
  21. package/dist/terraform/modules/data/aurora-postgres/README.md +92 -0
  22. package/dist/terraform/modules/data/aurora-postgres/main.tf +185 -0
  23. package/dist/terraform/modules/data/aurora-postgres/outputs.tf +30 -0
  24. package/dist/terraform/modules/data/aurora-postgres/variables.tf +114 -0
  25. package/dist/terraform/modules/data/bedrock-knowledge-base/main.tf +102 -0
  26. package/dist/terraform/modules/data/s3-buckets/main.tf +91 -0
  27. package/dist/terraform/modules/foundation/cognito/main.tf +377 -0
  28. package/dist/terraform/modules/foundation/cognito/outputs.tf +29 -0
  29. package/dist/terraform/modules/foundation/cognito/variables.tf +124 -0
  30. package/dist/terraform/modules/foundation/dns/main.tf +49 -0
  31. package/dist/terraform/modules/foundation/kms/main.tf +49 -0
  32. package/dist/terraform/modules/foundation/vpc/main.tf +137 -0
  33. package/dist/terraform/modules/foundation/vpc/outputs.tf +14 -0
  34. package/dist/terraform/modules/foundation/vpc/variables.tf +40 -0
  35. package/dist/terraform/modules/thinkwork/main.tf +212 -0
  36. package/dist/terraform/modules/thinkwork/outputs.tf +87 -0
  37. package/dist/terraform/modules/thinkwork/variables.tf +241 -0
  38. package/dist/terraform/schema.graphql +199 -0
  39. 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
+ }