thinkwork-cli 0.13.2 → 1.0.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 -14
- package/dist/{api-client-JYZXYQJR.js → api-client-4P4JZOBX.js} +1 -1
- package/dist/{chunk-Z2F6RJFR.js → chunk-4OMDWYZQ.js} +4 -5
- package/dist/{chunk-ILZKG5WV.js → chunk-AD2NIWB5.js} +434 -69
- package/dist/chunk-MWQ7RO76.js +137 -0
- package/dist/{chunk-DVPBI5HM.js → chunk-OLUQ4U2Z.js} +26 -26
- package/dist/{chunk-QOERT44A.js → chunk-YQCYD6NE.js} +57 -44
- package/dist/cli.js +4400 -16431
- package/dist/commands/enterprise/templates/deploy-repo/scripts/apply-release.mjs +0 -1
- package/dist/commands/enterprise/templates/deploy-repo/scripts/smoke.mjs +0 -1
- package/dist/commands/enterprise/templates/deploy-repo/terraform/main.tf +0 -14
- package/dist/{dist-es-CRV46D5B.js → dist-es-2OTNTL4N.js} +4 -4
- package/dist/{dist-es-ENBUC3JP.js → dist-es-2XVAH4BQ.js} +2 -2
- package/dist/{dist-es-DCY4IC3Y.js → dist-es-3DPDUJAH.js} +2 -2
- package/dist/{dist-es-G7PTG75M.js → dist-es-BNZTBBVC.js} +1 -1
- package/dist/{dist-es-GKDBE7E6.js → dist-es-CD2RBDAC.js} +1 -1
- package/dist/{dist-es-R7FGL2IB.js → dist-es-I4FW57PS.js} +1 -1
- package/dist/{dist-es-USORBXZ5.js → dist-es-KVFWJNDY.js} +4 -4
- package/dist/{dist-es-HY3535OI.js → dist-es-PS73DWNL.js} +2 -2
- package/dist/{dist-es-LDE437AM.js → dist-es-S3NZQFGT.js} +1 -1
- package/dist/{dist-es-G54LBMRN.js → dist-es-SGCA4P32.js} +1 -1
- package/dist/{dist-es-Q57SZXZ7.js → dist-es-WBM6J4RA.js} +2 -2
- package/dist/{dist-es-P26FWFLT.js → dist-es-ZGVADYIF.js} +8 -8
- package/dist/drizzle/0227_analyst_reader_role.sql +17 -26
- package/dist/drizzle/0230_analyst_rls.sql +28 -45
- package/dist/drizzle/0261_native_auth_control_plane.sql +7 -3
- package/dist/drizzle/0270_tool_execution_ledger.sql +115 -0
- package/dist/drizzle/0271_ontology_twin_declarations.sql +47 -0
- package/dist/drizzle/0272_identity_graph_projection_cursor.sql +23 -0
- package/dist/drizzle/0273_twin_materialization_suggestions.sql +29 -0
- package/dist/drizzle/0274_tenant_mcp_twin_keys.sql +59 -0
- package/dist/drizzle/0275_identity_bulk_rebuild_fence.sql +30 -0
- package/dist/drizzle/0276_tool_execution_ledger_allow_cascade_delete.sql +18 -0
- package/dist/drizzle/0277_knowledge_base_sources.sql +107 -0
- package/dist/drizzle/0278_kb_page_transcription.sql +55 -0
- package/dist/drizzle/0279_mcp_servers_off_plugin_provenance.sql +93 -0
- package/dist/drizzle/0280_work_item_external_refs_drop_plane_provider.sql +46 -0
- package/dist/drizzle/0281_tenant_mcp_twin_keys_expiry_suffix.sql +9 -0
- package/dist/drizzle/0282_tenant_mcp_twin_keys_grants.sql +24 -0
- package/dist/drizzle/0283_agents_agentcore_runtime_dispatch.sql +13 -0
- package/dist/drizzle/0284_user_brain_claims.sql +69 -0
- package/dist/drizzle/0285_tenant_mcp_twin_keys_trusted_subsystem.sql +30 -0
- package/dist/drizzle/0286_brain_analytics_key.sql +26 -0
- package/dist/drizzle/0287_brain_subject.sql +25 -0
- package/dist/drizzle/0288_multi_lane_identities.sql +25 -0
- package/dist/{loadSso-DJVB7BXE.js → loadSso-ETCXTR4I.js} +2 -2
- package/dist/{loadSso-JAMNA3WY.js → loadSso-UBD7KUHE.js} +2 -2
- package/dist/plugins/README.md +2 -2
- package/dist/plugins/catalog/package.json +0 -3
- package/dist/plugins/catalog/src/__tests__/build-catalog.test.ts +6 -3
- package/dist/plugins/catalog/src/__tests__/catalog.test.ts +21 -21
- package/dist/plugins/catalog/src/__tests__/contracts.test.ts +102 -25
- package/dist/plugins/catalog/src/__tests__/plugin-package.test.ts +7 -6
- package/dist/plugins/catalog/src/__tests__/plugin-registry.test.ts +0 -15
- package/dist/plugins/catalog/src/contracts.ts +15 -0
- package/dist/plugins/catalog/src/registry/generated-first-party.ts +0 -18
- package/dist/plugins/n8n/test/integrated-app-contract.test.ts +1 -1
- package/dist/plugins/twenty/package.json +2 -1
- package/dist/plugins/twenty/scripts/LAMBDA_DEPLOY.md +114 -0
- package/dist/plugins/twenty/scripts/build-lambda.sh +29 -0
- package/dist/plugins/twenty/scripts/configure-product-views.ts +195 -0
- package/dist/plugins/twenty/scripts/lambda-handler.ts +49 -0
- package/dist/plugins/twenty/scripts/lib/__tests__/load-attachments.test.ts +38 -0
- package/dist/plugins/twenty/scripts/lib/__tests__/load-records.test.ts +145 -2
- package/dist/plugins/twenty/scripts/lib/__tests__/mappers.test.ts +397 -151
- package/dist/plugins/twenty/scripts/lib/__tests__/members-ensure.test.ts +28 -129
- package/dist/plugins/twenty/scripts/lib/__tests__/provision-members-db.test.ts +10 -0
- package/dist/plugins/twenty/scripts/lib/__tests__/schema-ensure.test.ts +13 -8
- package/dist/plugins/twenty/scripts/lib/lastmile-reader.ts +222 -2
- package/dist/plugins/twenty/scripts/lib/load-attachments.ts +29 -0
- package/dist/plugins/twenty/scripts/lib/load-records.ts +160 -43
- package/dist/plugins/twenty/scripts/lib/mappers.ts +368 -152
- package/dist/plugins/twenty/scripts/lib/members-ensure.ts +43 -268
- package/dist/plugins/twenty/scripts/lib/provision-members-db.ts +57 -0
- package/dist/plugins/twenty/scripts/lib/schema-ensure.ts +136 -31
- package/dist/plugins/twenty/scripts/migrate-lastmile.ts +254 -152
- package/dist/plugins/twenty/scripts/provision-twenty-members.ts +44 -1
- package/dist/plugins/twenty/scripts/purge-lastmile-import.ts +178 -0
- package/dist/plugins/twenty/scripts/restructure-products.ts +312 -0
- package/dist/plugins/twenty/scripts/retag-stage-values.ts +362 -0
- package/dist/plugins/twenty/test/manifest.test.ts +2 -4
- package/dist/scripts/post-deploy.sh +79 -1
- package/dist/{signin-UGDWTGS4.js → signin-5QZNRSOO.js} +2 -2
- package/dist/{sso-oidc-Q4VXANXY.js → sso-oidc-XK673P54.js} +2 -2
- package/dist/{sts-CWLSE3GH.js → sts-OXI2UALF.js} +6 -134
- package/dist/{sts-DNZLNVOV.js → sts-SQW4NBK3.js} +6 -134
- package/dist/terraform/examples/greenfield/main.tf +233 -175
- package/dist/terraform/examples/greenfield/terraform.tfvars.example +12 -8
- package/dist/terraform/modules/app/agentcore-admin/main.tf +47 -0
- package/dist/terraform/modules/app/agentcore-code-interpreter/README.md +39 -47
- package/dist/terraform/modules/app/agentcore-code-interpreter/main.tf +13 -66
- package/dist/terraform/modules/app/agentcore-identity/main.tf +106 -26
- package/dist/terraform/modules/app/agentcore-identity/outputs.tf +6 -6
- package/dist/terraform/modules/app/agentcore-identity/scripts/bootstrap_twenty_oauth_client.sh +216 -99
- package/dist/terraform/modules/app/agentcore-identity/scripts/delete_identity.sh +6 -9
- package/dist/terraform/modules/app/agentcore-identity/scripts/delete_workload_identity.sh +19 -0
- package/dist/terraform/modules/app/agentcore-identity/scripts/ensure_workload_identity.sh +29 -0
- package/dist/terraform/modules/app/agentcore-identity/scripts/read_identity.sh +22 -7
- package/dist/terraform/modules/app/agentcore-identity/scripts/reconcile_twenty_identity.sh +17 -46
- package/dist/terraform/modules/app/agentcore-identity/scripts/reconcile_twenty_provider.mjs +65 -3
- package/dist/terraform/modules/app/agentcore-identity/tests/tainted-marker/after.tf +15 -0
- package/dist/terraform/modules/app/agentcore-identity/tests/tainted-marker/before.tf +7 -0
- package/dist/terraform/modules/app/agentcore-identity/tests/tainted-marker/identity-after.tf +8 -0
- package/dist/terraform/modules/app/agentcore-identity/tests/tainted-marker/identity-before.tf +9 -0
- package/dist/terraform/modules/app/agentcore-identity/tests/tainted-marker/thinkwork-after.tf +3 -0
- package/dist/terraform/modules/app/agentcore-identity/tests/tainted-marker/thinkwork-before.tf +3 -0
- package/dist/terraform/modules/app/agentcore-identity/variables.tf +6 -0
- package/dist/terraform/modules/app/agentcore-memory/README.md +46 -9
- package/dist/terraform/modules/app/agentcore-memory/main.tf +27 -10
- package/dist/terraform/modules/app/agentcore-memory/scripts/create_or_find_memory.sh +156 -24
- package/dist/terraform/modules/app/agentcore-pi/README.md +1 -3
- package/dist/terraform/modules/app/agentcore-pi/main.tf +42 -52
- package/dist/terraform/modules/app/agentcore-pi/outputs.tf +3 -3
- package/dist/terraform/modules/app/agentcore-pi/scripts/reconcile_pi_runtime.mjs +175 -0
- package/dist/terraform/modules/app/agentcore-pi/variables.tf +35 -28
- package/dist/terraform/modules/app/agentcore-platform/main.tf +68 -0
- package/dist/terraform/modules/app/agentcore-runtime/main.tf +4 -20
- package/dist/terraform/modules/app/capability-broker/main.tf +2 -2
- package/dist/terraform/modules/app/capability-broker/variables.tf +11 -0
- package/dist/terraform/modules/app/deployment-control-plane/README.md +264 -0
- package/dist/terraform/modules/app/deployment-control-plane/buildspec.yml +4 -1
- package/dist/terraform/modules/app/deployment-control-plane/main.tf +94 -0
- package/dist/terraform/modules/app/deployment-control-plane/outputs.tf +5 -0
- package/dist/terraform/modules/app/deployment-control-plane/runner.py +2641 -124
- package/dist/terraform/modules/app/deployment-control-plane/test_runner_bundle.py +2034 -154
- package/dist/terraform/modules/app/deployment-control-plane/variables.tf +33 -0
- package/dist/terraform/modules/app/lambda-api/auth-state.tf +72 -0
- package/dist/terraform/modules/app/lambda-api/chat-latency-observability.tf +466 -0
- package/dist/terraform/modules/app/lambda-api/dispatch-dlq.tf +55 -0
- package/dist/terraform/modules/app/lambda-api/handlers.tf +411 -723
- package/dist/terraform/modules/app/lambda-api/iam-grouped.tf +262 -198
- package/dist/terraform/modules/app/lambda-api/main.tf +51 -13
- package/dist/terraform/modules/app/lambda-api/mcp-oauth.tf +4 -181
- package/dist/terraform/modules/app/lambda-api/memory-alarms.tf +3 -28
- package/dist/terraform/modules/app/lambda-api/oauth-secrets.tf +1 -2
- package/dist/terraform/modules/app/lambda-api/outputs.tf +17 -45
- package/dist/terraform/modules/app/lambda-api/turn-assertion.tf +45 -0
- package/dist/terraform/modules/app/lambda-api/variables.tf +219 -126
- package/dist/{plugins/n8n/terraform → terraform/modules/app}/n8n/README.md +1 -1
- package/dist/{plugins/n8n/terraform → terraform/modules/app}/n8n/main.tf +18 -18
- package/dist/terraform/modules/app/sandbox-log-scrubber/README.md +11 -10
- package/dist/terraform/modules/app/sandbox-log-scrubber/main.tf +5 -5
- package/dist/terraform/modules/app/static-site/main.tf +1 -1
- package/dist/terraform/modules/app/www-dns/main.tf +9 -27
- package/dist/terraform/modules/app/www-dns/variables.tf +4 -10
- package/dist/terraform/modules/data/aurora-postgres/README.md +34 -0
- package/dist/terraform/modules/data/aurora-postgres/alarms.tf +82 -0
- package/dist/terraform/modules/data/aurora-postgres/main.tf +27 -57
- package/dist/terraform/modules/data/aurora-postgres/outputs.tf +0 -9
- package/dist/terraform/modules/data/aurora-postgres/variables.tf +41 -0
- package/dist/terraform/modules/data/s3-buckets/main.tf +5 -6
- package/dist/terraform/modules/foundation/cognito/main.tf +81 -8
- package/dist/terraform/modules/foundation/cognito/outputs.tf +5 -0
- package/dist/terraform/modules/foundation/cognito/variables.tf +24 -12
- package/dist/terraform/modules/thinkwork/README.md +5 -1
- package/dist/terraform/modules/thinkwork/main.tf +376 -374
- package/dist/terraform/modules/thinkwork/outputs.tf +12 -158
- package/dist/terraform/modules/thinkwork/variables.tf +246 -89
- package/dist/workspace-defaults/files/INSTRUCTIONS.md +9 -11
- package/dist/workspace-defaults/files/MEMORY_GUIDE.md +38 -35
- package/dist/workspace-defaults/files/ROUTER.md +1 -1
- package/dist/workspace-defaults/files/TOOLS.md +9 -39
- package/dist/workspace-defaults/files/agents/analyst/INSTRUCTIONS.md +6 -3
- package/dist/workspace-defaults/files/catalog-skills/brain-brief-builder/SKILL.md +165 -0
- package/dist/workspace-defaults/files/catalog-skills/n8n-workflow-operator/SKILL.md +75 -0
- package/dist/workspace-defaults/files/catalog-skills/n8n-workflow-operator/references/mcp-tooling.md +40 -0
- package/dist/workspace-defaults/files/catalog-skills/n8n-workflow-operator/references/validation-and-handoff.md +40 -0
- package/dist/workspace-defaults/files/catalog-skills/n8n-workflow-operator/references/workflow-authoring.md +46 -0
- package/dist/workspace-defaults/files/skills/artifact-builder/references/crm-dashboard.md +1 -1
- package/dist/workspace-defaults/files/skills/document-composer/SKILL.md +22 -3
- package/package.json +1 -1
- package/dist/drizzle/0261_managed_harness_multiplayer.sql +0 -313
- package/dist/drizzle/0262_parallel_harness_threads.sql +0 -9
- package/dist/drizzle/0264_harness_tool_execution_ledger.sql +0 -113
- package/dist/plugins/company-data/README.md +0 -49
- package/dist/plugins/company-data/package.json +0 -21
- package/dist/plugins/company-data/src/index.ts +0 -30
- package/dist/plugins/company-data/src/manifest.ts +0 -26
- package/dist/plugins/company-data/test/manifest.test.ts +0 -116
- package/dist/plugins/company-data/tsconfig.json +0 -8
- package/dist/plugins/company-etl/README.md +0 -46
- package/dist/plugins/company-etl/package.json +0 -21
- package/dist/plugins/company-etl/src/index.ts +0 -29
- package/dist/plugins/company-etl/src/manifest.ts +0 -26
- package/dist/plugins/company-etl/test/manifest.test.ts +0 -108
- package/dist/plugins/company-etl/tsconfig.json +0 -8
- package/dist/plugins/lastmile/README.md +0 -50
- package/dist/plugins/lastmile/package.json +0 -23
- package/dist/plugins/lastmile/smoke/lastmile-plugin-smoke.mjs +0 -661
- package/dist/plugins/lastmile/src/api/tasks-adapter.ts +0 -572
- package/dist/plugins/lastmile/src/discovery.fixture.ts +0 -64
- package/dist/plugins/lastmile/src/index.ts +0 -46
- package/dist/plugins/lastmile/src/manifest.ts +0 -139
- package/dist/plugins/lastmile/test/api/tasks-adapter.test.ts +0 -266
- package/dist/plugins/lastmile/test/discovery.test.ts +0 -88
- package/dist/plugins/lastmile/tsconfig.json +0 -8
- package/dist/plugins/n8n/smoke/n8n-managed-app-smoke.mjs +0 -409
- package/dist/plugins/twenty/smoke/twenty-managed-app-smoke.mjs +0 -359
- package/dist/terraform/modules/app/agentcore-code-interpreter/Dockerfile.sandbox-base +0 -61
- package/dist/terraform/modules/app/agentcore-code-interpreter/sandbox/sitecustomize.py +0 -227
- package/dist/terraform/modules/app/agentcore-code-interpreter/sandbox/test_sitecustomize.py +0 -277
- package/dist/terraform/modules/app/agentcore-code-interpreter/scripts/build_and_push_sandbox_base.sh +0 -70
- package/dist/terraform/modules/app/agentcore-gateway/main.tf +0 -305
- package/dist/terraform/modules/app/agentcore-gateway/outputs.tf +0 -44
- package/dist/terraform/modules/app/agentcore-gateway/scripts/delete_gateway.sh +0 -93
- package/dist/terraform/modules/app/agentcore-gateway/scripts/read_gateway.sh +0 -33
- package/dist/terraform/modules/app/agentcore-gateway/scripts/reconcile_gateway.sh +0 -543
- package/dist/terraform/modules/app/agentcore-gateway/variables.tf +0 -70
- package/dist/terraform/modules/app/agentcore-harness/main.tf +0 -330
- package/dist/terraform/modules/app/agentcore-harness/outputs.tf +0 -72
- package/dist/terraform/modules/app/agentcore-harness/scripts/delete_harness.sh +0 -5
- package/dist/terraform/modules/app/agentcore-harness/scripts/harness-lifecycle.mjs +0 -430
- package/dist/terraform/modules/app/agentcore-harness/scripts/harness-readback.mjs +0 -180
- package/dist/terraform/modules/app/agentcore-harness/scripts/harness-tool-contract.mjs +0 -193
- package/dist/terraform/modules/app/agentcore-harness/scripts/prune_harness.sh +0 -5
- package/dist/terraform/modules/app/agentcore-harness/scripts/read_harness.sh +0 -5
- package/dist/terraform/modules/app/agentcore-harness/scripts/reconcile_harness.sh +0 -5
- package/dist/terraform/modules/app/agentcore-harness/variables.tf +0 -96
- package/dist/terraform/modules/app/hindsight-memory/README.md +0 -89
- package/dist/terraform/modules/app/hindsight-memory/main.tf +0 -411
- package/dist/terraform/modules/app/lambda-api/agentcore-harness-alarms.tf +0 -88
- package/dist/terraform/modules/app/lambda-api/agentcore-identity-alarms.tf +0 -91
- package/dist/terraform/modules/data/bedrock-knowledge-base/main.tf +0 -102
- /package/dist/{plugins/n8n/terraform → terraform/modules/app}/n8n/outputs.tf +0 -0
- /package/dist/{plugins/n8n/terraform → terraform/modules/app}/n8n/scripts/sync-database.py +0 -0
- /package/dist/{plugins/n8n/terraform → terraform/modules/app}/n8n/scripts/test_sync_database.py +0 -0
- /package/dist/{plugins/n8n/terraform → terraform/modules/app}/n8n/variables.tf +0 -0
- /package/dist/{plugins/twenty/terraform → terraform/modules/app}/twenty/README.md +0 -0
- /package/dist/{plugins/twenty/terraform → terraform/modules/app}/twenty/main.tf +0 -0
- /package/dist/{plugins/twenty/terraform → terraform/modules/app}/twenty/outputs.tf +0 -0
- /package/dist/{plugins/twenty/terraform → terraform/modules/app}/twenty/variables.tf +0 -0
|
@@ -3,12 +3,11 @@
|
|
|
3
3
|
#
|
|
4
4
|
# Responsibilities:
|
|
5
5
|
# 1. ACM certificate in us-east-1 covering apex + www
|
|
6
|
-
# (+ optional
|
|
6
|
+
# (+ optional admin + optional app + optional computer).
|
|
7
7
|
# 2. Cloudflare DNS records for ACM DNS validation.
|
|
8
8
|
# 3. Apex CNAME in Cloudflare → primary CloudFront distribution (DNS-only).
|
|
9
9
|
# 4. Second CloudFront distribution fronting an S3 website-redirect bucket
|
|
10
10
|
# that 301s www.<domain> → https://<domain>, plus its Cloudflare CNAME.
|
|
11
|
-
# 5. Optional docs.<domain> CNAME → docs CloudFront distribution.
|
|
12
11
|
# 6. Optional admin.<domain> CNAME → admin CloudFront distribution.
|
|
13
12
|
# 7. Optional app.<domain> CNAME → end-user app CloudFront distribution.
|
|
14
13
|
# 8. Optional computer.<domain> → app.<domain> 301 compatibility redirect.
|
|
@@ -37,7 +36,6 @@ terraform {
|
|
|
37
36
|
locals {
|
|
38
37
|
apex = var.domain
|
|
39
38
|
www = "www.${var.domain}"
|
|
40
|
-
docs = "docs.${var.domain}"
|
|
41
39
|
admin = "admin.${var.domain}"
|
|
42
40
|
app = "app.${var.domain}"
|
|
43
41
|
computer = "computer.${var.domain}"
|
|
@@ -47,7 +45,7 @@ locals {
|
|
|
47
45
|
n8n = "n8n.${var.domain}"
|
|
48
46
|
name_id = replace(var.domain, ".", "-")
|
|
49
47
|
|
|
50
|
-
# ACM SANs: always include www, conditionally include
|
|
48
|
+
# ACM SANs: always include www, conditionally include admin, computer,
|
|
51
49
|
# and api. The Computer iframe sandbox and CRM ALB use their own certificates
|
|
52
50
|
# so adding or rotating those hosts never forces a replacement of this shared
|
|
53
51
|
# production certificate.
|
|
@@ -56,11 +54,16 @@ locals {
|
|
|
56
54
|
# depend on the cert, so the cert mustn't depend on those outputs.
|
|
57
55
|
cert_sans = concat(
|
|
58
56
|
[local.www],
|
|
59
|
-
var.include_docs ? [local.docs] : [],
|
|
60
57
|
var.include_admin ? [local.admin] : [],
|
|
61
58
|
var.include_app ? [local.app] : [],
|
|
62
59
|
var.include_computer ? [local.computer] : [],
|
|
63
60
|
var.include_api ? [local.api] : [],
|
|
61
|
+
# Retired docs.<domain> SAN, retained on stacks deployed before THINK-702.
|
|
62
|
+
# Shrinking the SAN list forces a create-before-destroy replacement of this
|
|
63
|
+
# shared certificate, which cannot be ordered against the thinkwork module
|
|
64
|
+
# (the two modules feed each other) and fails the plan with a cycle. A dead
|
|
65
|
+
# SAN costs nothing and its validation CNAME stays in place for renewals.
|
|
66
|
+
var.retain_docs_san ? ["docs.${var.domain}"] : [],
|
|
64
67
|
)
|
|
65
68
|
|
|
66
69
|
# Existing CNAME records stay gated on non-empty targets because their target
|
|
@@ -68,7 +71,6 @@ locals {
|
|
|
68
71
|
# such as app/computer compatibility/sandbox/crm CNAMEs must gate only on
|
|
69
72
|
# explicit booleans so Terraform can plan the resource count before the new
|
|
70
73
|
# load balancer exists.
|
|
71
|
-
create_docs_record = var.include_docs && var.docs_cloudfront_domain_name != ""
|
|
72
74
|
create_admin_record = var.include_admin && var.admin_cloudfront_domain_name != ""
|
|
73
75
|
create_app_record = var.include_app
|
|
74
76
|
create_computer_record = var.include_computer
|
|
@@ -78,7 +80,7 @@ locals {
|
|
|
78
80
|
}
|
|
79
81
|
|
|
80
82
|
################################################################################
|
|
81
|
-
# ACM certificate (us-east-1, covers apex + www [+
|
|
83
|
+
# ACM certificate (us-east-1, covers apex + www [+ optional subdomains])
|
|
82
84
|
################################################################################
|
|
83
85
|
|
|
84
86
|
resource "aws_acm_certificate" "www" {
|
|
@@ -244,26 +246,6 @@ resource "cloudflare_record" "www_redirect" {
|
|
|
244
246
|
comment = "thinkwork-${var.stage} www → redirect distribution"
|
|
245
247
|
}
|
|
246
248
|
|
|
247
|
-
################################################################################
|
|
248
|
-
# docs.<domain> → docs CloudFront distribution (optional)
|
|
249
|
-
#
|
|
250
|
-
# Created only when the greenfield stack wires docs_cloudfront_domain_name.
|
|
251
|
-
# The docs CloudFront alias picks up the same ACM cert via certificate_arn
|
|
252
|
-
# on the thinkwork module's docs_site.
|
|
253
|
-
################################################################################
|
|
254
|
-
|
|
255
|
-
resource "cloudflare_record" "docs" {
|
|
256
|
-
count = local.create_docs_record ? 1 : 0
|
|
257
|
-
|
|
258
|
-
zone_id = var.cloudflare_zone_id
|
|
259
|
-
name = local.docs
|
|
260
|
-
content = var.docs_cloudfront_domain_name
|
|
261
|
-
type = "CNAME"
|
|
262
|
-
ttl = 300
|
|
263
|
-
proxied = false
|
|
264
|
-
comment = "thinkwork-${var.stage} docs → CloudFront"
|
|
265
|
-
}
|
|
266
|
-
|
|
267
249
|
################################################################################
|
|
268
250
|
# admin.<domain> → admin CloudFront distribution (optional)
|
|
269
251
|
################################################################################
|
|
@@ -18,20 +18,14 @@ variable "cloudfront_domain_name" {
|
|
|
18
18
|
type = string
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
variable "
|
|
22
|
-
description = "When true,
|
|
21
|
+
variable "retain_docs_san" {
|
|
22
|
+
description = "When true, keep the retired docs.<domain> SAN on the shared ACM certificate. Set on stacks deployed before THINK-702 so removing the docs site does not force a certificate replacement (which cycles against the thinkwork module). New stacks leave this false."
|
|
23
23
|
type = bool
|
|
24
24
|
default = false
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
variable "docs_cloudfront_domain_name" {
|
|
28
|
-
description = "CloudFront distribution domain name for the docs site. Used as the target for the docs.<domain> Cloudflare CNAME when include_docs is true."
|
|
29
|
-
type = string
|
|
30
|
-
default = ""
|
|
31
|
-
}
|
|
32
|
-
|
|
33
27
|
variable "include_admin" {
|
|
34
|
-
description = "When true, add admin.<domain> to the ACM cert SANs and create a Cloudflare CNAME for it.
|
|
28
|
+
description = "When true, add admin.<domain> to the ACM cert SANs and create a Cloudflare CNAME for it. Gated on a plain bool (not a CloudFront output) to keep the dependency graph acyclic."
|
|
35
29
|
type = bool
|
|
36
30
|
default = false
|
|
37
31
|
}
|
|
@@ -67,7 +61,7 @@ variable "computer_cloudfront_domain_name" {
|
|
|
67
61
|
}
|
|
68
62
|
|
|
69
63
|
variable "include_computer_sandbox" {
|
|
70
|
-
description = "When true, add sandbox.<domain> to the ACM cert SANs and create a Cloudflare CNAME for it.
|
|
64
|
+
description = "When true, add sandbox.<domain> to the ACM cert SANs and create a Cloudflare CNAME for it. Gated on a plain bool (not a CloudFront output) to keep the dependency graph acyclic."
|
|
71
65
|
type = bool
|
|
72
66
|
default = false
|
|
73
67
|
}
|
|
@@ -78,6 +78,8 @@ module "database" {
|
|
|
78
78
|
| `max_capacity` | number | `2` | Aurora max ACU (aurora-serverless only) |
|
|
79
79
|
| `rds_instance_class` | string | `"db.t4g.micro"` | RDS instance class (rds-postgres only) |
|
|
80
80
|
| `rds_allocated_storage` | number | `20` | Storage in GB (rds-postgres only) |
|
|
81
|
+
| `publicly_accessible` | bool | `true` | Public IP on the cluster instance / RDS instance. `false` once every client is inside the VPC (THINK-1082) |
|
|
82
|
+
| `public_ingress_cidrs` | list(string) | `["0.0.0.0/0"]` | CIDR tcp/5432 ingress seeded on a **new** database security group. `[]` = none. Does not revoke a live rule (see below) |
|
|
81
83
|
| `deletion_protection` | bool | `null` | Auto: `true` for Aurora, `false` for RDS |
|
|
82
84
|
|
|
83
85
|
## Outputs
|
|
@@ -90,3 +92,35 @@ module "database" {
|
|
|
90
92
|
| `db_security_group_id` | Security group ID |
|
|
91
93
|
| `database_url` | Full connection string (sensitive) |
|
|
92
94
|
| `database_engine` | Which engine is running |
|
|
95
|
+
|
|
96
|
+
## Closing the public port (THINK-1082)
|
|
97
|
+
|
|
98
|
+
By default the database gets a public endpoint (`publicly_accessible = true`)
|
|
99
|
+
and its security group is seeded with a world-open `0.0.0.0/0 -> 5432` ingress.
|
|
100
|
+
Every client that has no `vpc_config` (the api Lambdas, the deployment runner's
|
|
101
|
+
`psql`) depends on that rule. Revoking it before those clients move inside the
|
|
102
|
+
VPC takes the app down (auth-me -> DB timeout -> 401).
|
|
103
|
+
|
|
104
|
+
Two facts govern the close:
|
|
105
|
+
|
|
106
|
+
1. The security group has `lifecycle { ignore_changes = [name, ingress] }` so
|
|
107
|
+
app modules can attach standalone `aws_security_group_rule` grants for their
|
|
108
|
+
own security groups (ECS tasks, the runner, the api-lambda group). Because of
|
|
109
|
+
that, setting `public_ingress_cidrs = []` on an existing stage changes what
|
|
110
|
+
Terraform would create and stops it re-seeding the rule, but **does not
|
|
111
|
+
revoke the rule that already exists**. The operator revokes it by hand, once:
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
aws ec2 revoke-security-group-ingress --group-id <thinkwork-<stage>-db-sg> \
|
|
115
|
+
--protocol tcp --port 5432 --cidr 0.0.0.0/0
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
2. `publicly_accessible = false` removes the public IP (a modify on the
|
|
119
|
+
instance, applied immediately). Do it in the same step as, or after, the
|
|
120
|
+
ingress close — never before the clients are in the VPC.
|
|
121
|
+
|
|
122
|
+
The RDS Data API (`enable_http_endpoint = true`) keeps working throughout: it
|
|
123
|
+
needs IAM, not a network path, so operator SQL and the Pi session store are
|
|
124
|
+
unaffected. The stage-level order (Lambdas first, runner second, port last) is
|
|
125
|
+
in `modules/app/deployment-control-plane/README.md`, "Closing the database
|
|
126
|
+
port".
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
################################################################################
|
|
2
|
+
# Aurora Serverless v2 saturation alarms
|
|
3
|
+
#
|
|
4
|
+
# STATISTIC MATTERS HERE. RDS publishes CPUUtilization from 1-second samples,
|
|
5
|
+
# so the `Maximum` statistic over any period reports the single busiest second
|
|
6
|
+
# in that period — on Serverless v2 that reads as ~100% essentially forever
|
|
7
|
+
# and tells you nothing. An operator chasing "the database is pegged" off the
|
|
8
|
+
# Maximum series is chasing an artifact of the sampling rate, not load.
|
|
9
|
+
#
|
|
10
|
+
# Both alarms below therefore use `Average`, which is the statistic that
|
|
11
|
+
# actually tracks sustained saturation:
|
|
12
|
+
# - CPUUtilization — average CPU across the 5-minute period.
|
|
13
|
+
# - ACUUtilization — how much of the configured max_capacity ACU ceiling
|
|
14
|
+
# the cluster is consuming. This is the one that
|
|
15
|
+
# matters for Serverless v2: sustained high ACU means
|
|
16
|
+
# the cluster is near its scaling ceiling and the next
|
|
17
|
+
# load step has nowhere to go.
|
|
18
|
+
#
|
|
19
|
+
# 3 × 5-minute evaluation periods (15 minutes of sustained pressure) so a
|
|
20
|
+
# migration, a backfill, or a burst of agent traffic does not page anyone.
|
|
21
|
+
#
|
|
22
|
+
# alarm_actions are empty by module convention — this module owns no SNS
|
|
23
|
+
# topic, and the app module's only topic is the cost-alert channel (see
|
|
24
|
+
# terraform/modules/app/lambda-api/memory-alarms.tf for the same reasoning).
|
|
25
|
+
# Operators subscribe a topic when a stage needs paging.
|
|
26
|
+
#
|
|
27
|
+
# Aurora-only: ACUUtilization does not exist for the rds-postgres dev/test
|
|
28
|
+
# engine, and the pairing is what makes these useful, so both are gated on
|
|
29
|
+
# local.use_aurora.
|
|
30
|
+
################################################################################
|
|
31
|
+
|
|
32
|
+
resource "aws_cloudwatch_metric_alarm" "aurora_cpu_utilization_high" {
|
|
33
|
+
count = local.use_aurora ? 1 : 0
|
|
34
|
+
|
|
35
|
+
alarm_name = "thinkwork-${var.stage}-db-cpu-utilization-high"
|
|
36
|
+
alarm_description = "Aurora writer instance averaged >${var.cpu_utilization_alarm_threshold}% CPU for 15 minutes. Statistic is Average by design — the Maximum series is ~100% at all times because RDS samples CPU every second."
|
|
37
|
+
|
|
38
|
+
namespace = "AWS/RDS"
|
|
39
|
+
metric_name = "CPUUtilization"
|
|
40
|
+
statistic = "Average"
|
|
41
|
+
|
|
42
|
+
period = 300
|
|
43
|
+
evaluation_periods = 3
|
|
44
|
+
threshold = var.cpu_utilization_alarm_threshold
|
|
45
|
+
comparison_operator = "GreaterThanThreshold"
|
|
46
|
+
treat_missing_data = "notBreaching"
|
|
47
|
+
alarm_actions = []
|
|
48
|
+
|
|
49
|
+
dimensions = {
|
|
50
|
+
DBInstanceIdentifier = aws_rds_cluster_instance.main[0].identifier
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
tags = {
|
|
54
|
+
Name = "thinkwork-${var.stage}-db-cpu-utilization-high"
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
resource "aws_cloudwatch_metric_alarm" "aurora_acu_utilization_high" {
|
|
59
|
+
count = local.use_aurora ? 1 : 0
|
|
60
|
+
|
|
61
|
+
alarm_description = "Aurora Serverless v2 averaged >${var.acu_utilization_alarm_threshold}% of its max_capacity ACU ceiling for 15 minutes — the cluster is close to having nowhere left to scale. Raise max_capacity or shed load."
|
|
62
|
+
alarm_name = "thinkwork-${var.stage}-db-acu-utilization-high"
|
|
63
|
+
|
|
64
|
+
namespace = "AWS/RDS"
|
|
65
|
+
metric_name = "ACUUtilization"
|
|
66
|
+
statistic = "Average"
|
|
67
|
+
|
|
68
|
+
period = 300
|
|
69
|
+
evaluation_periods = 3
|
|
70
|
+
threshold = var.acu_utilization_alarm_threshold
|
|
71
|
+
comparison_operator = "GreaterThanThreshold"
|
|
72
|
+
treat_missing_data = "notBreaching"
|
|
73
|
+
alarm_actions = []
|
|
74
|
+
|
|
75
|
+
dimensions = {
|
|
76
|
+
DBInstanceIdentifier = aws_rds_cluster_instance.main[0].identifier
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
tags = {
|
|
80
|
+
Name = "thinkwork-${var.stage}-db-acu-utilization-high"
|
|
81
|
+
}
|
|
82
|
+
}
|
|
@@ -53,12 +53,20 @@ resource "aws_security_group" "db" {
|
|
|
53
53
|
description = "Security group for Thinkwork PostgreSQL database"
|
|
54
54
|
vpc_id = var.vpc_id
|
|
55
55
|
|
|
56
|
-
ingress
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
56
|
+
# THINK-1082: the inline CIDR ingress is now driven by public_ingress_cidrs
|
|
57
|
+
# (default ["0.0.0.0/0"] = today's world-open rule; [] = none). Because
|
|
58
|
+
# ingress is in ignore_changes below, this only shapes a NEW group and stops
|
|
59
|
+
# Terraform from re-seeding the rule — it never revokes a live one.
|
|
60
|
+
dynamic "ingress" {
|
|
61
|
+
for_each = length(var.public_ingress_cidrs) > 0 ? [1] : []
|
|
62
|
+
|
|
63
|
+
content {
|
|
64
|
+
from_port = 5432
|
|
65
|
+
to_port = 5432
|
|
66
|
+
protocol = "tcp"
|
|
67
|
+
cidr_blocks = var.public_ingress_cidrs
|
|
68
|
+
description = "PostgreSQL access"
|
|
69
|
+
}
|
|
62
70
|
}
|
|
63
71
|
|
|
64
72
|
egress {
|
|
@@ -76,7 +84,10 @@ resource "aws_security_group" "db" {
|
|
|
76
84
|
# App modules grant their own task security groups database access through
|
|
77
85
|
# standalone aws_security_group_rule resources. Keep this shared group from
|
|
78
86
|
# trying to reconcile those externally managed ingress rules during targeted
|
|
79
|
-
# managed-app deploys.
|
|
87
|
+
# managed-app deploys. Consequence (THINK-1082): setting
|
|
88
|
+
# public_ingress_cidrs = [] on a live stage does not revoke the existing
|
|
89
|
+
# 0.0.0.0/0 rule — the operator revokes it by hand once the clients are
|
|
90
|
+
# in the VPC (see README.md "Closing the public port").
|
|
80
91
|
ignore_changes = [name, ingress]
|
|
81
92
|
}
|
|
82
93
|
}
|
|
@@ -117,15 +128,11 @@ resource "aws_rds_cluster" "main" {
|
|
|
117
128
|
deletion_protection = local.deletion_protection
|
|
118
129
|
storage_encrypted = true
|
|
119
130
|
|
|
120
|
-
#
|
|
121
|
-
#
|
|
122
|
-
#
|
|
123
|
-
#
|
|
124
|
-
#
|
|
125
|
-
# migration (0229) landed inside that pending window the role would
|
|
126
|
-
# refuse BOTH password (grant applied) and IAM (flag still pending)
|
|
127
|
-
# login. Deploys on this platform are CLI/CI-driven and expect
|
|
128
|
-
# immediate semantics.
|
|
131
|
+
# RDS IAM database authentication: roles granted rds_iam log in with
|
|
132
|
+
# per-connect auth tokens instead of a stored password. Cluster-level
|
|
133
|
+
# flag; applies without downtime. apply_immediately is required — the
|
|
134
|
+
# provider default defers cluster modifications to the maintenance
|
|
135
|
+
# window, which can strand a role between the GRANT and the flag.
|
|
129
136
|
iam_database_authentication_enabled = true
|
|
130
137
|
apply_immediately = true
|
|
131
138
|
|
|
@@ -154,7 +161,7 @@ resource "aws_rds_cluster_instance" "main" {
|
|
|
154
161
|
engine = aws_rds_cluster.main[0].engine
|
|
155
162
|
engine_version = aws_rds_cluster.main[0].engine_version
|
|
156
163
|
|
|
157
|
-
publicly_accessible =
|
|
164
|
+
publicly_accessible = var.publicly_accessible
|
|
158
165
|
db_subnet_group_name = aws_db_subnet_group.main[0].name
|
|
159
166
|
|
|
160
167
|
tags = {
|
|
@@ -188,13 +195,12 @@ resource "aws_db_instance" "main" {
|
|
|
188
195
|
db_subnet_group_name = aws_db_subnet_group.main[0].name
|
|
189
196
|
vpc_security_group_ids = [aws_security_group.db[0].id]
|
|
190
197
|
|
|
191
|
-
publicly_accessible =
|
|
198
|
+
publicly_accessible = var.publicly_accessible
|
|
192
199
|
skip_final_snapshot = true
|
|
193
200
|
deletion_protection = local.deletion_protection
|
|
194
201
|
|
|
195
|
-
#
|
|
196
|
-
#
|
|
197
|
-
# apply_immediately for the same both-paths-stranded reason as the
|
|
202
|
+
# Keep the rds-postgres dev/test engine at IAM-auth parity with the
|
|
203
|
+
# Aurora cluster. apply_immediately for the same stranding reason as the
|
|
198
204
|
# Aurora cluster block above.
|
|
199
205
|
iam_database_authentication_enabled = true
|
|
200
206
|
apply_immediately = true
|
|
@@ -377,39 +383,3 @@ resource "aws_secretsmanager_secret" "compliance_reader" {
|
|
|
377
383
|
Role = "compliance_reader"
|
|
378
384
|
}
|
|
379
385
|
}
|
|
380
|
-
|
|
381
|
-
################################################################################
|
|
382
|
-
# Secrets Manager — Analyst Reader Role Credentials (THINK-228 U2)
|
|
383
|
-
#
|
|
384
|
-
# Container for the analyst_reader Aurora role used by the analyst
|
|
385
|
-
# query-broker Lambda. Terraform owns the SECRET CONTAINER; the operator
|
|
386
|
-
# owns the SECRET VALUE (populated by scripts/bootstrap-analyst-roles.sh,
|
|
387
|
-
# which also applies drizzle/0227_analyst_reader_role.sql to create the
|
|
388
|
-
# matching hardened read-only role).
|
|
389
|
-
################################################################################
|
|
390
|
-
|
|
391
|
-
resource "aws_secretsmanager_secret" "analyst_reader" {
|
|
392
|
-
count = local.create ? 1 : 0
|
|
393
|
-
name = "thinkwork/${var.stage}/analyst/reader-credentials"
|
|
394
|
-
|
|
395
|
-
tags = {
|
|
396
|
-
Name = "thinkwork-${var.stage}-analyst-reader-credentials"
|
|
397
|
-
Role = "analyst_reader"
|
|
398
|
-
}
|
|
399
|
-
}
|
|
400
|
-
|
|
401
|
-
# Broker caller credential for the analyst query broker (THINK-228 U3).
|
|
402
|
-
# The connector row (tenant_mcp_servers, seeded by
|
|
403
|
-
# scripts/provision-analyst-connector.mts) references this ARN as its
|
|
404
|
-
# auth_config.secretRef; the broker Lambda validates incoming Bearer
|
|
405
|
-
# tokens against it. Value shape: JSON {token, tenantId} — populated by
|
|
406
|
-
# the provisioning script, never by Terraform.
|
|
407
|
-
resource "aws_secretsmanager_secret" "analyst_broker" {
|
|
408
|
-
count = local.create ? 1 : 0
|
|
409
|
-
name = "thinkwork/${var.stage}/analyst/broker-credential"
|
|
410
|
-
|
|
411
|
-
tags = {
|
|
412
|
-
Name = "thinkwork-${var.stage}-analyst-broker-credential"
|
|
413
|
-
Role = "analyst_query_broker"
|
|
414
|
-
}
|
|
415
|
-
}
|
|
@@ -68,12 +68,3 @@ output "compliance_reader_secret_arn" {
|
|
|
68
68
|
value = local.create ? aws_secretsmanager_secret.compliance_reader[0].arn : ""
|
|
69
69
|
}
|
|
70
70
|
|
|
71
|
-
output "analyst_reader_secret_arn" {
|
|
72
|
-
description = "Secrets Manager ARN for the analyst_reader Aurora role (THINK-228 U2). Empty string when create_database = false."
|
|
73
|
-
value = local.create ? aws_secretsmanager_secret.analyst_reader[0].arn : ""
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
output "analyst_broker_secret_arn" {
|
|
77
|
-
description = "Secrets Manager ARN for the analyst query-broker caller credential (THINK-228 U3). Empty string when create_database = false."
|
|
78
|
-
value = local.create ? aws_secretsmanager_secret.analyst_broker[0].arn : ""
|
|
79
|
-
}
|
|
@@ -113,6 +113,31 @@ variable "deletion_protection" {
|
|
|
113
113
|
default = null
|
|
114
114
|
}
|
|
115
115
|
|
|
116
|
+
# ---------------------------------------------------------------------------
|
|
117
|
+
# Network exposure (THINK-1082). Defaults reproduce today's posture: a public
|
|
118
|
+
# endpoint plus a world-open 0.0.0.0/0 -> 5432 ingress. A stage whose database
|
|
119
|
+
# clients (api Lambdas, deployment runner, ECS tasks) all reach the cluster by
|
|
120
|
+
# security group closes the port by setting public_ingress_cidrs = [] and
|
|
121
|
+
# publicly_accessible = false. NOTE: the DB security group ignores ingress
|
|
122
|
+
# drift (lifecycle.ignore_changes) so that app modules can own their own
|
|
123
|
+
# standalone rules — which means shrinking this list does NOT revoke a rule
|
|
124
|
+
# that already exists on a live group. The operator revokes it by hand once;
|
|
125
|
+
# this variable governs what a fresh group is created with and what
|
|
126
|
+
# Terraform stops re-seeding. See README.md "Closing the public port".
|
|
127
|
+
# ---------------------------------------------------------------------------
|
|
128
|
+
|
|
129
|
+
variable "publicly_accessible" {
|
|
130
|
+
description = "Whether the cluster instance / RDS instance gets a public IP (publicly_accessible). Default true = today's posture. Set false once every client reaches the database inside the VPC by security group."
|
|
131
|
+
type = bool
|
|
132
|
+
default = true
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
variable "public_ingress_cidrs" {
|
|
136
|
+
description = "CIDR blocks seeded as tcp/5432 ingress on the database security group when it is created. Default [\"0.0.0.0/0\"] = today's world-open rule. [] seeds no inline rule (clients are admitted by security-group-sourced rules instead). Because the group ignores ingress drift, changing this on an existing stage does not revoke the live rule — revoke it by hand after the clients are inside the VPC."
|
|
137
|
+
type = list(string)
|
|
138
|
+
default = ["0.0.0.0/0"]
|
|
139
|
+
}
|
|
140
|
+
|
|
116
141
|
# ---------------------------------------------------------------------------
|
|
117
142
|
# aws_s3 Aurora extension (optional — set backups_bucket_arn to enable)
|
|
118
143
|
# ---------------------------------------------------------------------------
|
|
@@ -123,6 +148,22 @@ variable "backups_bucket_arn" {
|
|
|
123
148
|
default = null
|
|
124
149
|
}
|
|
125
150
|
|
|
151
|
+
# ---------------------------------------------------------------------------
|
|
152
|
+
# CloudWatch saturation alarms (aurora-serverless engine only; see alarms.tf)
|
|
153
|
+
# ---------------------------------------------------------------------------
|
|
154
|
+
|
|
155
|
+
variable "cpu_utilization_alarm_threshold" {
|
|
156
|
+
description = "Average CPUUtilization percentage that, sustained for 3x5min, raises the Aurora CPU alarm. Evaluated against the Average statistic — the Maximum statistic is meaningless on RDS (1-second sampling pins it near 100%)."
|
|
157
|
+
type = number
|
|
158
|
+
default = 70
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
variable "acu_utilization_alarm_threshold" {
|
|
162
|
+
description = "Average ACUUtilization percentage (share of max_capacity consumed) that, sustained for 3x5min, raises the Aurora Serverless v2 scaling-headroom alarm."
|
|
163
|
+
type = number
|
|
164
|
+
default = 85
|
|
165
|
+
}
|
|
166
|
+
|
|
126
167
|
variable "enable_aws_s3" {
|
|
127
168
|
description = "Plan-time gate for the aws_s3 Aurora extension IAM role. Set true when Aurora should be allowed to PutObject into backups_bucket_arn. Only effective when database_engine = 'aurora-serverless'. Defaults to false to preserve the prior 'opt-in by passing backups_bucket_arn' shape when this variable is omitted."
|
|
128
169
|
type = bool
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
################################################################################
|
|
2
2
|
# S3 Buckets — Data Module
|
|
3
3
|
#
|
|
4
|
-
# Creates the primary S3 bucket for skills, artifacts,
|
|
4
|
+
# Creates the primary S3 bucket for skills, artifacts,
|
|
5
5
|
# and email inbound storage. HTTPS-only enforcement + SES inbound policy.
|
|
6
6
|
################################################################################
|
|
7
7
|
|
|
@@ -109,11 +109,10 @@ output "bucket_arn" {
|
|
|
109
109
|
value = aws_s3_bucket.main.arn
|
|
110
110
|
}
|
|
111
111
|
|
|
112
|
-
#
|
|
113
|
-
#
|
|
114
|
-
#
|
|
115
|
-
#
|
|
116
|
-
# in which a leaked object key is useful.
|
|
112
|
+
# Janitor for the RETIRED analyst query broker's staged results. The broker
|
|
113
|
+
# is gone, but stages that ran it still hold CSVs under analyst-staging/.
|
|
114
|
+
# Keeping the expiry rule drains them; deleting it would strand that data
|
|
115
|
+
# with no TTL. Safe to remove once every stage's prefix is empty.
|
|
117
116
|
resource "aws_s3_bucket_lifecycle_configuration" "analyst_staging" {
|
|
118
117
|
bucket = aws_s3_bucket.main.id
|
|
119
118
|
|
|
@@ -42,11 +42,6 @@ locals {
|
|
|
42
42
|
logout_urls = var.mobile_logout_urls
|
|
43
43
|
refresh_token_days = 90
|
|
44
44
|
}
|
|
45
|
-
desktop = {
|
|
46
|
-
callback_urls = var.desktop_callback_urls
|
|
47
|
-
logout_urls = var.desktop_callback_urls
|
|
48
|
-
refresh_token_days = 30
|
|
49
|
-
}
|
|
50
45
|
cli = {
|
|
51
46
|
callback_urls = var.cli_callback_urls
|
|
52
47
|
logout_urls = var.cli_logout_urls
|
|
@@ -526,7 +521,6 @@ resource "aws_cognito_user_pool_client" "auth_route" {
|
|
|
526
521
|
|
|
527
522
|
write_attributes = [
|
|
528
523
|
"email",
|
|
529
|
-
"email_verified",
|
|
530
524
|
"name",
|
|
531
525
|
"custom:tenant_id",
|
|
532
526
|
"custom:entra_tenant_id",
|
|
@@ -608,8 +602,8 @@ resource "aws_cognito_user_pool_client" "admin" {
|
|
|
608
602
|
|
|
609
603
|
supported_identity_providers = local.identity_providers
|
|
610
604
|
|
|
611
|
-
callback_urls = distinct(concat(var.admin_callback_urls, var.
|
|
612
|
-
logout_urls = distinct(concat(var.admin_logout_urls, var.
|
|
605
|
+
callback_urls = distinct(concat(var.admin_callback_urls, var.cli_callback_urls))
|
|
606
|
+
logout_urls = distinct(concat(var.admin_logout_urls, var.cli_logout_urls))
|
|
613
607
|
|
|
614
608
|
access_token_validity = 1
|
|
615
609
|
id_token_validity = 1
|
|
@@ -696,6 +690,76 @@ resource "aws_cognito_user_pool_client" "mobile" {
|
|
|
696
690
|
]
|
|
697
691
|
}
|
|
698
692
|
|
|
693
|
+
################################################################################
|
|
694
|
+
# App Client — Company Brain console (U13, optional; ship-inert default off)
|
|
695
|
+
#
|
|
696
|
+
# Same OAuth flows/scopes/providers as the web client family. Gated on
|
|
697
|
+
# console_client_enabled so merging this creates nothing; enablement requires
|
|
698
|
+
# the console's callback URLs to be registered here FIRST (redirect_mismatch
|
|
699
|
+
# otherwise).
|
|
700
|
+
################################################################################
|
|
701
|
+
|
|
702
|
+
resource "aws_cognito_user_pool_client" "console" {
|
|
703
|
+
count = local.create && var.console_client_enabled ? 1 : 0
|
|
704
|
+
name = "ThinkworkConsole"
|
|
705
|
+
user_pool_id = aws_cognito_user_pool.main[0].id
|
|
706
|
+
|
|
707
|
+
generate_secret = false
|
|
708
|
+
allowed_oauth_flows_user_pool_client = true
|
|
709
|
+
allowed_oauth_flows = ["code"]
|
|
710
|
+
allowed_oauth_scopes = ["openid", "email", "profile"]
|
|
711
|
+
|
|
712
|
+
explicit_auth_flows = [
|
|
713
|
+
"ALLOW_USER_PASSWORD_AUTH",
|
|
714
|
+
"ALLOW_USER_SRP_AUTH",
|
|
715
|
+
"ALLOW_REFRESH_TOKEN_AUTH",
|
|
716
|
+
]
|
|
717
|
+
|
|
718
|
+
supported_identity_providers = local.identity_providers
|
|
719
|
+
|
|
720
|
+
callback_urls = distinct(var.console_callback_urls)
|
|
721
|
+
logout_urls = distinct(var.console_logout_urls)
|
|
722
|
+
|
|
723
|
+
enable_token_revocation = true
|
|
724
|
+
prevent_user_existence_errors = "ENABLED"
|
|
725
|
+
access_token_validity = 1
|
|
726
|
+
id_token_validity = 1
|
|
727
|
+
refresh_token_validity = 30
|
|
728
|
+
|
|
729
|
+
token_validity_units {
|
|
730
|
+
access_token = "hours"
|
|
731
|
+
id_token = "hours"
|
|
732
|
+
refresh_token = "days"
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
read_attributes = [
|
|
736
|
+
"email",
|
|
737
|
+
"email_verified",
|
|
738
|
+
"name",
|
|
739
|
+
"custom:tenant_id",
|
|
740
|
+
]
|
|
741
|
+
|
|
742
|
+
write_attributes = [
|
|
743
|
+
"email",
|
|
744
|
+
"name",
|
|
745
|
+
"custom:tenant_id",
|
|
746
|
+
]
|
|
747
|
+
|
|
748
|
+
lifecycle {
|
|
749
|
+
precondition {
|
|
750
|
+
condition = length(var.console_callback_urls) > 0
|
|
751
|
+
error_message = "console_callback_urls must be non-empty when console_client_enabled — register the console URL before first login or Cognito fails with redirect_mismatch."
|
|
752
|
+
}
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
depends_on = [
|
|
756
|
+
aws_cognito_identity_provider.google,
|
|
757
|
+
aws_cognito_identity_provider.microsoft_organizations,
|
|
758
|
+
aws_cognito_identity_provider.oidc,
|
|
759
|
+
aws_cognito_identity_provider.saml,
|
|
760
|
+
]
|
|
761
|
+
}
|
|
762
|
+
|
|
699
763
|
################################################################################
|
|
700
764
|
# Identity Pool
|
|
701
765
|
################################################################################
|
|
@@ -726,6 +790,15 @@ resource "aws_cognito_identity_pool" "main" {
|
|
|
726
790
|
}
|
|
727
791
|
}
|
|
728
792
|
|
|
793
|
+
dynamic "cognito_identity_providers" {
|
|
794
|
+
for_each = aws_cognito_user_pool_client.console
|
|
795
|
+
content {
|
|
796
|
+
client_id = cognito_identity_providers.value.id
|
|
797
|
+
provider_name = "cognito-idp.${var.region}.amazonaws.com/${aws_cognito_user_pool.main[0].id}"
|
|
798
|
+
server_side_token_check = true
|
|
799
|
+
}
|
|
800
|
+
}
|
|
801
|
+
|
|
729
802
|
tags = {
|
|
730
803
|
Name = "thinkwork-${var.stage}-identity-pool"
|
|
731
804
|
}
|
|
@@ -18,6 +18,11 @@ output "mobile_client_id" {
|
|
|
18
18
|
value = local.mobile_client_id
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
+
output "console_client_id" {
|
|
22
|
+
description = "App client ID for the optional Company Brain console client (null while console_client_enabled = false)"
|
|
23
|
+
value = one(aws_cognito_user_pool_client.console[*].id)
|
|
24
|
+
}
|
|
25
|
+
|
|
21
26
|
output "identity_pool_id" {
|
|
22
27
|
description = "Identity pool ID (created or existing)"
|
|
23
28
|
value = local.identity_pool_id
|
|
@@ -147,8 +147,8 @@ variable "microsoft_oauth_tenant" {
|
|
|
147
147
|
default = ""
|
|
148
148
|
|
|
149
149
|
validation {
|
|
150
|
-
condition = var.microsoft_oauth_tenant == "" || can(regex("^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$", var.microsoft_oauth_tenant))
|
|
151
|
-
error_message = "microsoft_oauth_tenant must be an Entra directory GUID."
|
|
150
|
+
condition = var.microsoft_oauth_tenant == "" || contains(["common", "organizations", "consumers"], var.microsoft_oauth_tenant) || can(regex("^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$", var.microsoft_oauth_tenant))
|
|
151
|
+
error_message = "microsoft_oauth_tenant must be an Entra directory GUID or one of the Cognito-supported aliases: common, organizations, consumers."
|
|
152
152
|
}
|
|
153
153
|
}
|
|
154
154
|
|
|
@@ -295,16 +295,6 @@ variable "admin_logout_urls" {
|
|
|
295
295
|
]
|
|
296
296
|
}
|
|
297
297
|
|
|
298
|
-
variable "desktop_callback_urls" {
|
|
299
|
-
description = "OAuth callback and logout URLs for the desktop client that reuses the ThinkworkAdmin public Cognito client."
|
|
300
|
-
type = list(string)
|
|
301
|
-
default = [
|
|
302
|
-
"thinkwork://oauth/callback",
|
|
303
|
-
"thinkwork-dev://oauth/callback",
|
|
304
|
-
"thinkwork-canary://oauth/callback",
|
|
305
|
-
]
|
|
306
|
-
}
|
|
307
|
-
|
|
308
298
|
variable "cli_callback_urls" {
|
|
309
299
|
description = "OAuth loopback callbacks reserved for the CLI client family."
|
|
310
300
|
type = list(string)
|
|
@@ -323,6 +313,28 @@ variable "cli_logout_urls" {
|
|
|
323
313
|
]
|
|
324
314
|
}
|
|
325
315
|
|
|
316
|
+
# ---------------------------------------------------------------------------
|
|
317
|
+
# Company Brain console client (U13 — ship-inert, default off)
|
|
318
|
+
# ---------------------------------------------------------------------------
|
|
319
|
+
|
|
320
|
+
variable "console_client_enabled" {
|
|
321
|
+
description = "Create the optional Company Brain console app client. Default false = no client, console_client_id output is null (ship-inert until Eric reviews the console app)."
|
|
322
|
+
type = bool
|
|
323
|
+
default = false
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
variable "console_callback_urls" {
|
|
327
|
+
description = "OAuth callback URLs for the console client. MUST include the console's CloudFront/custom-domain URL BEFORE first login — Cognito rejects unregistered origins with redirect_mismatch. Required non-empty when console_client_enabled."
|
|
328
|
+
type = list(string)
|
|
329
|
+
default = []
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
variable "console_logout_urls" {
|
|
333
|
+
description = "OAuth logout URLs for the console client"
|
|
334
|
+
type = list(string)
|
|
335
|
+
default = []
|
|
336
|
+
}
|
|
337
|
+
|
|
326
338
|
variable "existing_auth_route_clients" {
|
|
327
339
|
description = "Safe route-client manifest when using a BYO Cognito pool. Keys use <family>:<route>."
|
|
328
340
|
type = map(object({
|