@aws/nx-plugin 0.108.0 → 0.109.1

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 (38) hide show
  1. package/package.json +2 -2
  2. package/src/py/fast-api/__snapshots__/generator.spec.ts.snap +167 -89
  3. package/src/py/lambda-function/__snapshots__/generator.spec.ts.snap +72 -16
  4. package/src/py/mcp-server/__snapshots__/generator.spec.ts.snap +11 -9
  5. package/src/py/strands-agent/__snapshots__/generator.spec.ts.snap +11 -9
  6. package/src/py/strands-agent/generator.js +95 -2
  7. package/src/py/strands-agent/generator.js.map +1 -1
  8. package/src/py/strands-agent/scripts/http/chat.ts.template +38 -0
  9. package/src/smithy/ts/api/__snapshots__/generator.spec.ts.snap +113 -61
  10. package/src/terraform/project/files/application/scripts/aws-config.ts.template +41 -0
  11. package/src/terraform/project/files/application/scripts/bootstrap.ts.template +91 -0
  12. package/src/terraform/project/files/application/scripts/init.ts.template +44 -0
  13. package/src/terraform/project/generator.js +16 -11
  14. package/src/terraform/project/generator.js.map +1 -1
  15. package/src/trpc/backend/__snapshots__/generator.spec.ts.snap +167 -89
  16. package/src/ts/lambda-function/__snapshots__/generator.spec.ts.snap +72 -16
  17. package/src/ts/mcp-server/__snapshots__/generator.spec.ts.snap +11 -9
  18. package/src/ts/react-website/app/__snapshots__/generator.spec.ts.snap +7 -0
  19. package/src/ts/strands-agent/__snapshots__/generator.spec.ts.snap +11 -9
  20. package/src/ts/strands-agent/generator.js +31 -0
  21. package/src/ts/strands-agent/generator.js.map +1 -1
  22. package/src/ts/strands-agent/scripts/http/chat.ts.template +35 -0
  23. package/src/utils/agent-core-constructs/files/terraform/app/agent-core/__nameKebabCase__/__nameKebabCase__.tf.template +14 -12
  24. package/src/utils/api-constructs/files/terraform/app/apis/http/__apiNameKebabCase__/__apiNameKebabCase__.tf.template +28 -15
  25. package/src/utils/api-constructs/files/terraform/app/apis/rest/__apiNameKebabCase__/__apiNameKebabCase__.tf.template +29 -16
  26. package/src/utils/files/terraform/scripts/reset-runtime-config.ts.template +25 -0
  27. package/src/utils/files/terraform/src/core/asset-bucket/asset-bucket.tf.template +203 -0
  28. package/src/utils/files/terraform/src/core/runtime-config/appconfig/appconfig.tf.template +42 -39
  29. package/src/utils/files/terraform/src/core/runtime-config/appconfig-deployment/appconfig-deployment.tf.template +138 -0
  30. package/src/utils/files/terraform/src/core/runtime-config/entry/entry.tf.template +26 -90
  31. package/src/utils/files/terraform/src/core/runtime-config/read/read.tf.template +61 -5
  32. package/src/utils/function-constructs/files/terraform/app/lambda-functions/__functionNameKebabCase__/__functionNameKebabCase__.tf.template +35 -7
  33. package/src/utils/shared-constructs.js +4 -2
  34. package/src/utils/shared-constructs.js.map +1 -1
  35. package/src/utils/versions.d.ts +7 -3
  36. package/src/utils/versions.js +6 -2
  37. package/src/utils/versions.js.map +1 -1
  38. package/src/utils/website-constructs/files/terraform/core/static-website/static-website.tf.template +7 -0
@@ -716,6 +716,11 @@ variable "additional_iam_policy_statements" {
716
716
  default = []
717
717
  }
718
718
 
719
+ variable "asset_bucket_name" {
720
+ description = "Name of the shared asset S3 bucket used to stage the Lambda deployment zip. Instantiate the \`core/asset-bucket\` module once per deployment and pass its \`bucket_name\` output here."
721
+ type = string
722
+ }
723
+
719
724
  # CORS Configuration (passed to core module)
720
725
 
721
726
  variable "cors_allow_headers" {
@@ -754,20 +759,34 @@ variable "tags" {
754
759
  data "aws_region" "current" {}
755
760
  data "aws_caller_identity" "current" {}
756
761
 
762
+ resource "random_string" "suffix" {
763
+ length = 8
764
+ special = false
765
+ upper = false
766
+ }
767
+
757
768
  # Resources
758
769
 
759
- # Create Lambda ZIP file from the FastAPI bundle directory
770
+ # Create Lambda ZIP file from the bundle directory
760
771
  data "archive_file" "lambda_zip" {
761
772
  type = "zip"
762
773
  source_dir = "\${path.module}/../../../../../../../dist/test-api/backend/bundle"
763
774
  output_path = "\${path.module}/../../../../../../../dist/packages/common/terraform/apis/test-api/lambda.zip"
764
775
  }
765
776
 
777
+ resource "aws_s3_object" "lambda_zip" {
778
+ bucket = var.asset_bucket_name
779
+ key = "apis/test-api/\${data.archive_file.lambda_zip.output_base64sha256}.zip"
780
+ source = data.archive_file.lambda_zip.output_path
781
+ source_hash = data.archive_file.lambda_zip.output_base64sha256
782
+ etag = data.archive_file.lambda_zip.output_md5
783
+ }
784
+
766
785
  # Use the core REST API module
767
786
  module "rest_api" {
768
787
  source = "../../../core/api/rest-api"
769
788
 
770
- api_name = "TestApi"
789
+ api_name = "TestApi-\${random_string.suffix.result}"
771
790
  api_description = "TestApi REST API"
772
791
  stage_name = "prod"
773
792
  stage_auto_deploy = true
@@ -800,8 +819,10 @@ resource "aws_lambda_function" "api_lambda" {
800
819
  #checkov:skip=CKV_AWS_272:Code signing not required for this use case
801
820
  #checkov:skip=CKV_AWS_115:Concurrent execution limit not required for this use case
802
821
  #checkov:skip=CKV_AWS_173:Lambda environment variables encrypted by managed key
803
- filename = data.archive_file.lambda_zip.output_path
804
- function_name = "TestApiHandler"
822
+ s3_bucket = aws_s3_object.lambda_zip.bucket
823
+ s3_key = aws_s3_object.lambda_zip.key
824
+ s3_object_version = aws_s3_object.lambda_zip.version_id
825
+ function_name = "TestApiHandler-\${random_string.suffix.result}"
805
826
  role = aws_iam_role.lambda_execution_role.arn
806
827
  handler = "index.handler"
807
828
  runtime = "nodejs22.x"
@@ -826,7 +847,7 @@ resource "aws_lambda_function" "api_lambda" {
826
847
 
827
848
  # IAM role for Lambda execution
828
849
  resource "aws_iam_role" "lambda_execution_role" {
829
- name = "TestApiHandler-execution-role"
850
+ name = "TestApiHandler-execution-role-\${random_string.suffix.result}"
830
851
 
831
852
  assume_role_policy = jsonencode({
832
853
  Version = "2012-10-17"
@@ -859,7 +880,7 @@ resource "aws_iam_role_policy_attachment" "lambda_xray_execution" {
859
880
  # Additional IAM policies for Lambda (if provided)
860
881
  resource "aws_iam_role_policy" "lambda_additional_policies" {
861
882
  count = length(var.additional_iam_policy_statements) > 0 ? 1 : 0
862
- name = "TestApiHandler-additional-policies"
883
+ name = "TestApiHandler-additional-policies-\${random_string.suffix.result}"
863
884
  role = aws_iam_role.lambda_execution_role.id
864
885
 
865
886
  policy = jsonencode({
@@ -873,7 +894,7 @@ resource "aws_cloudwatch_log_group" "lambda_logs" {
873
894
  #checkov:skip=CKV_AWS_158:Using default CloudWatch log encryption
874
895
  #checkov:skip=CKV_AWS_338:Log retention set to forever
875
896
  #checkov:skip=CKV_AWS_66:Log retention set to forever
876
- name = "/aws/lambda/TestApiHandler"
897
+ name = "/aws/lambda/TestApiHandler-\${random_string.suffix.result}"
877
898
  tags = var.tags
878
899
  }
879
900
 
@@ -1053,14 +1074,6 @@ module "add_url_to_runtime_config" {
1053
1074
  depends_on = [aws_api_gateway_stage.api_stage]
1054
1075
  }
1055
1076
 
1056
- module "appconfig" {
1057
- source = "../../../core/runtime-config/appconfig"
1058
-
1059
- application_name = "TestApi-runtime-config"
1060
-
1061
- depends_on = [module.add_url_to_runtime_config]
1062
- }
1063
-
1064
1077
  # Outputs
1065
1078
 
1066
1079
  # API Gateway Outputs (from core module)
@@ -1897,6 +1910,11 @@ variable "additional_iam_policy_statements" {
1897
1910
  default = []
1898
1911
  }
1899
1912
 
1913
+ variable "asset_bucket_name" {
1914
+ description = "Name of the shared asset S3 bucket used to stage the Lambda deployment zip. Instantiate the \`core/asset-bucket\` module once per deployment and pass its \`bucket_name\` output here."
1915
+ type = string
1916
+ }
1917
+
1900
1918
  # CORS Configuration (passed to core module)
1901
1919
 
1902
1920
  variable "cors_allow_headers" {
@@ -1935,20 +1953,34 @@ variable "tags" {
1935
1953
  data "aws_region" "current" {}
1936
1954
  data "aws_caller_identity" "current" {}
1937
1955
 
1956
+ resource "random_string" "suffix" {
1957
+ length = 8
1958
+ special = false
1959
+ upper = false
1960
+ }
1961
+
1938
1962
  # Resources
1939
1963
 
1940
- # Create Lambda ZIP file from the FastAPI bundle directory
1964
+ # Create Lambda ZIP file from the bundle directory
1941
1965
  data "archive_file" "lambda_zip" {
1942
1966
  type = "zip"
1943
1967
  source_dir = "\${path.module}/../../../../../../../dist/test-api/backend/bundle"
1944
1968
  output_path = "\${path.module}/../../../../../../../dist/packages/common/terraform/apis/test-api/lambda.zip"
1945
1969
  }
1946
1970
 
1971
+ resource "aws_s3_object" "lambda_zip" {
1972
+ bucket = var.asset_bucket_name
1973
+ key = "apis/test-api/\${data.archive_file.lambda_zip.output_base64sha256}.zip"
1974
+ source = data.archive_file.lambda_zip.output_path
1975
+ source_hash = data.archive_file.lambda_zip.output_base64sha256
1976
+ etag = data.archive_file.lambda_zip.output_md5
1977
+ }
1978
+
1947
1979
  # Use the core REST API module
1948
1980
  module "rest_api" {
1949
1981
  source = "../../../core/api/rest-api"
1950
1982
 
1951
- api_name = "TestApi"
1983
+ api_name = "TestApi-\${random_string.suffix.result}"
1952
1984
  api_description = "TestApi REST API"
1953
1985
  stage_name = "prod"
1954
1986
  stage_auto_deploy = true
@@ -1981,8 +2013,10 @@ resource "aws_lambda_function" "api_lambda" {
1981
2013
  #checkov:skip=CKV_AWS_272:Code signing not required for this use case
1982
2014
  #checkov:skip=CKV_AWS_115:Concurrent execution limit not required for this use case
1983
2015
  #checkov:skip=CKV_AWS_173:Lambda environment variables encrypted by managed key
1984
- filename = data.archive_file.lambda_zip.output_path
1985
- function_name = "TestApiHandler"
2016
+ s3_bucket = aws_s3_object.lambda_zip.bucket
2017
+ s3_key = aws_s3_object.lambda_zip.key
2018
+ s3_object_version = aws_s3_object.lambda_zip.version_id
2019
+ function_name = "TestApiHandler-\${random_string.suffix.result}"
1986
2020
  role = aws_iam_role.lambda_execution_role.arn
1987
2021
  handler = "index.handler"
1988
2022
  runtime = "nodejs22.x"
@@ -2007,7 +2041,7 @@ resource "aws_lambda_function" "api_lambda" {
2007
2041
 
2008
2042
  # IAM role for Lambda execution
2009
2043
  resource "aws_iam_role" "lambda_execution_role" {
2010
- name = "TestApiHandler-execution-role"
2044
+ name = "TestApiHandler-execution-role-\${random_string.suffix.result}"
2011
2045
 
2012
2046
  assume_role_policy = jsonencode({
2013
2047
  Version = "2012-10-17"
@@ -2040,7 +2074,7 @@ resource "aws_iam_role_policy_attachment" "lambda_xray_execution" {
2040
2074
  # Additional IAM policies for Lambda (if provided)
2041
2075
  resource "aws_iam_role_policy" "lambda_additional_policies" {
2042
2076
  count = length(var.additional_iam_policy_statements) > 0 ? 1 : 0
2043
- name = "TestApiHandler-additional-policies"
2077
+ name = "TestApiHandler-additional-policies-\${random_string.suffix.result}"
2044
2078
  role = aws_iam_role.lambda_execution_role.id
2045
2079
 
2046
2080
  policy = jsonencode({
@@ -2054,14 +2088,14 @@ resource "aws_cloudwatch_log_group" "lambda_logs" {
2054
2088
  #checkov:skip=CKV_AWS_158:Using default CloudWatch log encryption
2055
2089
  #checkov:skip=CKV_AWS_338:Log retention set to forever
2056
2090
  #checkov:skip=CKV_AWS_66:Log retention set to forever
2057
- name = "/aws/lambda/TestApiHandler"
2091
+ name = "/aws/lambda/TestApiHandler-\${random_string.suffix.result}"
2058
2092
  tags = var.tags
2059
2093
  }
2060
2094
 
2061
2095
 
2062
2096
  # Cognito User Pool Authorizer
2063
2097
  resource "aws_api_gateway_authorizer" "cognito_authorizer" {
2064
- name = "TestApiAuthorizer"
2098
+ name = "TestApiAuthorizer-\${random_string.suffix.result}"
2065
2099
  rest_api_id = module.rest_api.api_id
2066
2100
  type = "COGNITO_USER_POOLS"
2067
2101
  provider_arns = ["arn:aws:cognito-idp:\${data.aws_region.current.name}:\${data.aws_caller_identity.current.account_id}:userpool/\${var.user_pool_id}"]
@@ -2241,14 +2275,6 @@ module "add_url_to_runtime_config" {
2241
2275
  depends_on = [aws_api_gateway_stage.api_stage]
2242
2276
  }
2243
2277
 
2244
- module "appconfig" {
2245
- source = "../../../core/runtime-config/appconfig"
2246
-
2247
- application_name = "TestApi-runtime-config"
2248
-
2249
- depends_on = [module.add_url_to_runtime_config]
2250
- }
2251
-
2252
2278
  # Outputs
2253
2279
 
2254
2280
  # API Gateway Outputs (from core module)
@@ -2669,6 +2695,11 @@ variable "additional_iam_policy_statements" {
2669
2695
  default = []
2670
2696
  }
2671
2697
 
2698
+ variable "asset_bucket_name" {
2699
+ description = "Name of the shared asset S3 bucket used to stage the Lambda deployment zip. Instantiate the \`core/asset-bucket\` module once per deployment and pass its \`bucket_name\` output here."
2700
+ type = string
2701
+ }
2702
+
2672
2703
  # CORS Configuration (passed to core module)
2673
2704
 
2674
2705
  variable "cors_allow_headers" {
@@ -2707,20 +2738,34 @@ variable "tags" {
2707
2738
  data "aws_region" "current" {}
2708
2739
  data "aws_caller_identity" "current" {}
2709
2740
 
2741
+ resource "random_string" "suffix" {
2742
+ length = 8
2743
+ special = false
2744
+ upper = false
2745
+ }
2746
+
2710
2747
  # Resources
2711
2748
 
2712
- # Create Lambda ZIP file from the FastAPI bundle directory
2749
+ # Create Lambda ZIP file from the bundle directory
2713
2750
  data "archive_file" "lambda_zip" {
2714
2751
  type = "zip"
2715
2752
  source_dir = "\${path.module}/../../../../../../../dist/test-api/backend/bundle"
2716
2753
  output_path = "\${path.module}/../../../../../../../dist/packages/common/terraform/apis/test-api/lambda.zip"
2717
2754
  }
2718
2755
 
2756
+ resource "aws_s3_object" "lambda_zip" {
2757
+ bucket = var.asset_bucket_name
2758
+ key = "apis/test-api/\${data.archive_file.lambda_zip.output_base64sha256}.zip"
2759
+ source = data.archive_file.lambda_zip.output_path
2760
+ source_hash = data.archive_file.lambda_zip.output_base64sha256
2761
+ etag = data.archive_file.lambda_zip.output_md5
2762
+ }
2763
+
2719
2764
  # Use the core REST API module
2720
2765
  module "rest_api" {
2721
2766
  source = "../../../core/api/rest-api"
2722
2767
 
2723
- api_name = "TestApi"
2768
+ api_name = "TestApi-\${random_string.suffix.result}"
2724
2769
  api_description = "TestApi REST API"
2725
2770
  stage_name = "prod"
2726
2771
  stage_auto_deploy = true
@@ -2753,8 +2798,10 @@ resource "aws_lambda_function" "api_lambda" {
2753
2798
  #checkov:skip=CKV_AWS_272:Code signing not required for this use case
2754
2799
  #checkov:skip=CKV_AWS_115:Concurrent execution limit not required for this use case
2755
2800
  #checkov:skip=CKV_AWS_173:Lambda environment variables encrypted by managed key
2756
- filename = data.archive_file.lambda_zip.output_path
2757
- function_name = "TestApiHandler"
2801
+ s3_bucket = aws_s3_object.lambda_zip.bucket
2802
+ s3_key = aws_s3_object.lambda_zip.key
2803
+ s3_object_version = aws_s3_object.lambda_zip.version_id
2804
+ function_name = "TestApiHandler-\${random_string.suffix.result}"
2758
2805
  role = aws_iam_role.lambda_execution_role.arn
2759
2806
  handler = "index.handler"
2760
2807
  runtime = "nodejs22.x"
@@ -2779,7 +2826,7 @@ resource "aws_lambda_function" "api_lambda" {
2779
2826
 
2780
2827
  # IAM role for Lambda execution
2781
2828
  resource "aws_iam_role" "lambda_execution_role" {
2782
- name = "TestApiHandler-execution-role"
2829
+ name = "TestApiHandler-execution-role-\${random_string.suffix.result}"
2783
2830
 
2784
2831
  assume_role_policy = jsonencode({
2785
2832
  Version = "2012-10-17"
@@ -2812,7 +2859,7 @@ resource "aws_iam_role_policy_attachment" "lambda_xray_execution" {
2812
2859
  # Additional IAM policies for Lambda (if provided)
2813
2860
  resource "aws_iam_role_policy" "lambda_additional_policies" {
2814
2861
  count = length(var.additional_iam_policy_statements) > 0 ? 1 : 0
2815
- name = "TestApiHandler-additional-policies"
2862
+ name = "TestApiHandler-additional-policies-\${random_string.suffix.result}"
2816
2863
  role = aws_iam_role.lambda_execution_role.id
2817
2864
 
2818
2865
  policy = jsonencode({
@@ -2826,7 +2873,7 @@ resource "aws_cloudwatch_log_group" "lambda_logs" {
2826
2873
  #checkov:skip=CKV_AWS_158:Using default CloudWatch log encryption
2827
2874
  #checkov:skip=CKV_AWS_338:Log retention set to forever
2828
2875
  #checkov:skip=CKV_AWS_66:Log retention set to forever
2829
- name = "/aws/lambda/TestApiHandler"
2876
+ name = "/aws/lambda/TestApiHandler-\${random_string.suffix.result}"
2830
2877
  tags = var.tags
2831
2878
  }
2832
2879
 
@@ -3015,14 +3062,6 @@ module "add_url_to_runtime_config" {
3015
3062
  depends_on = [aws_api_gateway_stage.api_stage]
3016
3063
  }
3017
3064
 
3018
- module "appconfig" {
3019
- source = "../../../core/runtime-config/appconfig"
3020
-
3021
- application_name = "TestApi-runtime-config"
3022
-
3023
- depends_on = [module.add_url_to_runtime_config]
3024
- }
3025
-
3026
3065
  # Outputs
3027
3066
 
3028
3067
  # API Gateway Outputs (from core module)
@@ -3177,6 +3216,11 @@ variable "additional_iam_policy_statements" {
3177
3216
  default = []
3178
3217
  }
3179
3218
 
3219
+ variable "asset_bucket_name" {
3220
+ description = "Name of the shared asset S3 bucket used to stage the Lambda deployment zip. Instantiate the \`core/asset-bucket\` module once per deployment and pass its \`bucket_name\` output here."
3221
+ type = string
3222
+ }
3223
+
3180
3224
  # CORS Configuration (passed to core module)
3181
3225
 
3182
3226
  variable "cors_allow_headers" {
@@ -3215,20 +3259,34 @@ variable "tags" {
3215
3259
  data "aws_region" "current" {}
3216
3260
  data "aws_caller_identity" "current" {}
3217
3261
 
3262
+ resource "random_string" "suffix" {
3263
+ length = 8
3264
+ special = false
3265
+ upper = false
3266
+ }
3267
+
3218
3268
  # Resources
3219
3269
 
3220
- # Create Lambda ZIP file from the FastAPI bundle directory
3270
+ # Create Lambda ZIP file from the bundle directory
3221
3271
  data "archive_file" "lambda_zip" {
3222
3272
  type = "zip"
3223
3273
  source_dir = "\${path.module}/../../../../../../../dist/custom-api/backend/bundle"
3224
3274
  output_path = "\${path.module}/../../../../../../../dist/packages/common/terraform/apis/custom-api/lambda.zip"
3225
3275
  }
3226
3276
 
3277
+ resource "aws_s3_object" "lambda_zip" {
3278
+ bucket = var.asset_bucket_name
3279
+ key = "apis/custom-api/\${data.archive_file.lambda_zip.output_base64sha256}.zip"
3280
+ source = data.archive_file.lambda_zip.output_path
3281
+ source_hash = data.archive_file.lambda_zip.output_base64sha256
3282
+ etag = data.archive_file.lambda_zip.output_md5
3283
+ }
3284
+
3227
3285
  # Use the core REST API module
3228
3286
  module "rest_api" {
3229
3287
  source = "../../../core/api/rest-api"
3230
3288
 
3231
- api_name = "CustomApi"
3289
+ api_name = "CustomApi-\${random_string.suffix.result}"
3232
3290
  api_description = "CustomApi REST API"
3233
3291
  stage_name = "prod"
3234
3292
  stage_auto_deploy = true
@@ -3261,8 +3319,10 @@ resource "aws_lambda_function" "api_lambda" {
3261
3319
  #checkov:skip=CKV_AWS_272:Code signing not required for this use case
3262
3320
  #checkov:skip=CKV_AWS_115:Concurrent execution limit not required for this use case
3263
3321
  #checkov:skip=CKV_AWS_173:Lambda environment variables encrypted by managed key
3264
- filename = data.archive_file.lambda_zip.output_path
3265
- function_name = "CustomApiHandler"
3322
+ s3_bucket = aws_s3_object.lambda_zip.bucket
3323
+ s3_key = aws_s3_object.lambda_zip.key
3324
+ s3_object_version = aws_s3_object.lambda_zip.version_id
3325
+ function_name = "CustomApiHandler-\${random_string.suffix.result}"
3266
3326
  role = aws_iam_role.lambda_execution_role.arn
3267
3327
  handler = "index.handler"
3268
3328
  runtime = "nodejs22.x"
@@ -3287,7 +3347,7 @@ resource "aws_lambda_function" "api_lambda" {
3287
3347
 
3288
3348
  # IAM role for Lambda execution
3289
3349
  resource "aws_iam_role" "lambda_execution_role" {
3290
- name = "CustomApiHandler-execution-role"
3350
+ name = "CustomApiHandler-execution-role-\${random_string.suffix.result}"
3291
3351
 
3292
3352
  assume_role_policy = jsonencode({
3293
3353
  Version = "2012-10-17"
@@ -3320,7 +3380,7 @@ resource "aws_iam_role_policy_attachment" "lambda_xray_execution" {
3320
3380
  # Additional IAM policies for Lambda (if provided)
3321
3381
  resource "aws_iam_role_policy" "lambda_additional_policies" {
3322
3382
  count = length(var.additional_iam_policy_statements) > 0 ? 1 : 0
3323
- name = "CustomApiHandler-additional-policies"
3383
+ name = "CustomApiHandler-additional-policies-\${random_string.suffix.result}"
3324
3384
  role = aws_iam_role.lambda_execution_role.id
3325
3385
 
3326
3386
  policy = jsonencode({
@@ -3334,7 +3394,7 @@ resource "aws_cloudwatch_log_group" "lambda_logs" {
3334
3394
  #checkov:skip=CKV_AWS_158:Using default CloudWatch log encryption
3335
3395
  #checkov:skip=CKV_AWS_338:Log retention set to forever
3336
3396
  #checkov:skip=CKV_AWS_66:Log retention set to forever
3337
- name = "/aws/lambda/CustomApiHandler"
3397
+ name = "/aws/lambda/CustomApiHandler-\${random_string.suffix.result}"
3338
3398
  tags = var.tags
3339
3399
  }
3340
3400
 
@@ -3514,14 +3574,6 @@ module "add_url_to_runtime_config" {
3514
3574
  depends_on = [aws_api_gateway_stage.api_stage]
3515
3575
  }
3516
3576
 
3517
- module "appconfig" {
3518
- source = "../../../core/runtime-config/appconfig"
3519
-
3520
- application_name = "CustomApi-runtime-config"
3521
-
3522
- depends_on = [module.add_url_to_runtime_config]
3523
- }
3524
-
3525
3577
  # Outputs
3526
3578
 
3527
3579
  # API Gateway Outputs (from core module)
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Resolves the current AWS account ID + region using the standard
3
+ * AWS SDK credential + config chain (same chain CDK uses).
4
+ */
5
+ import { STSClient, GetCallerIdentityCommand } from '@aws-sdk/client-sts';
6
+ import { fromNodeProviderChain } from '@aws-sdk/credential-providers';
7
+ import {
8
+ NODE_REGION_CONFIG_FILE_OPTIONS,
9
+ NODE_REGION_CONFIG_OPTIONS,
10
+ } from '@smithy/config-resolver';
11
+ import { loadConfig } from '@smithy/node-config-provider';
12
+
13
+ export interface AwsConfig {
14
+ accountId: string;
15
+ region: string;
16
+ }
17
+
18
+ export async function resolveAwsConfig(): Promise<AwsConfig> {
19
+ const region = await loadConfig(
20
+ NODE_REGION_CONFIG_OPTIONS,
21
+ NODE_REGION_CONFIG_FILE_OPTIONS,
22
+ )();
23
+ if (!region) {
24
+ throw new Error(
25
+ 'Unable to resolve AWS region. Set the `AWS_REGION` environment variable or configure a default region via `aws configure`.',
26
+ );
27
+ }
28
+
29
+ const sts = new STSClient({
30
+ region,
31
+ credentials: fromNodeProviderChain(),
32
+ });
33
+ const identity = await sts.send(new GetCallerIdentityCommand({}));
34
+ if (!identity.Account) {
35
+ throw new Error(
36
+ 'Unable to resolve AWS account ID — no credentials found in the default provider chain.',
37
+ );
38
+ }
39
+
40
+ return { accountId: identity.Account, region };
41
+ }
@@ -0,0 +1,91 @@
1
+ /**
2
+ * Bootstraps the remote Terraform state bucket.
3
+ *
4
+ * Equivalent to `cdk bootstrap` — resolves account + region from the AWS
5
+ * SDK credential chain, pulls any existing bootstrap tfstate from S3,
6
+ * runs `terraform apply` in the `bootstrap` dir, then pushes the new
7
+ * state back to S3.
8
+ */
9
+ import { execFileSync } from 'node:child_process';
10
+ import { mkdirSync, readFileSync, writeFileSync } from 'node:fs';
11
+ import { dirname, join, resolve } from 'node:path';
12
+ import {
13
+ S3Client,
14
+ GetObjectCommand,
15
+ PutObjectCommand,
16
+ } from '@aws-sdk/client-s3';
17
+ import { fromNodeProviderChain } from '@aws-sdk/credential-providers';
18
+ import { resolveAwsConfig } from './aws-config';
19
+
20
+ const projectRootRel = process.argv[2];
21
+ if (!projectRootRel) {
22
+ throw new Error(
23
+ 'Expected the project root as argv[2] — this script is wired up by the generated `bootstrap` nx target and should be invoked through it.',
24
+ );
25
+ }
26
+ const workspaceRoot = process.cwd();
27
+ const projectRoot = resolve(workspaceRoot, projectRootRel);
28
+ const bootstrapDir = join(projectRoot, 'bootstrap');
29
+ const tfStatePath = join(
30
+ workspaceRoot,
31
+ 'dist',
32
+ projectRootRel,
33
+ 'terraform',
34
+ 'bootstrap.tfstate',
35
+ );
36
+
37
+ const main = async () => {
38
+ const { accountId, region } = await resolveAwsConfig();
39
+ const bucket = `${accountId}-tf-state-${region}`;
40
+ const key = 'bootstrap.tfstate';
41
+
42
+ const s3 = new S3Client({ region, credentials: fromNodeProviderChain() });
43
+
44
+ mkdirSync(dirname(tfStatePath), { recursive: true });
45
+
46
+ // Pull any existing bootstrap tfstate. First-time bootstrap has no
47
+ // remote state yet — fall through and let terraform apply create the
48
+ // bucket.
49
+ try {
50
+ const out = await s3.send(
51
+ new GetObjectCommand({ Bucket: bucket, Key: key }),
52
+ );
53
+ if (out.Body) {
54
+ writeFileSync(tfStatePath, await out.Body.transformToByteArray());
55
+ }
56
+ } catch (err: any) {
57
+ const name = err?.name ?? '';
58
+ const status = err?.$metadata?.httpStatusCode;
59
+ const firstRun =
60
+ name === 'NoSuchBucket' ||
61
+ name === 'NoSuchKey' ||
62
+ status === 404 ||
63
+ status === 403;
64
+ if (!firstRun) throw err;
65
+ }
66
+
67
+ execFileSync('terraform', ['init'], { cwd: bootstrapDir, stdio: 'inherit' });
68
+ execFileSync(
69
+ 'terraform',
70
+ [
71
+ 'apply',
72
+ '-auto-approve',
73
+ `-state=${tfStatePath}`,
74
+ `-var=aws_region=${region}`,
75
+ ],
76
+ { cwd: bootstrapDir, stdio: 'inherit' },
77
+ );
78
+
79
+ await s3.send(
80
+ new PutObjectCommand({
81
+ Bucket: bucket,
82
+ Key: key,
83
+ Body: readFileSync(tfStatePath),
84
+ }),
85
+ );
86
+ };
87
+
88
+ main().catch((err) => {
89
+ console.error(err);
90
+ process.exit(1);
91
+ });
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Runs `terraform init` against the remote S3 backend, resolving the
3
+ * backend config (bucket + region + per-env state key) from the AWS
4
+ * SDK credential chain. Pass the environment name via the `TF_ENV`
5
+ * env var (defaults to `dev`).
6
+ */
7
+ import { execFileSync } from 'node:child_process';
8
+ import { join, resolve } from 'node:path';
9
+ import { resolveAwsConfig } from './aws-config';
10
+
11
+ const STATE_KEY_PREFIX = '<%- stateKeyPrefix %>';
12
+
13
+ const projectRootRel = process.argv[2];
14
+ if (!projectRootRel) {
15
+ throw new Error(
16
+ 'Expected the project root as argv[2] — this script is wired up by the generated `init` nx target and should be invoked through it.',
17
+ );
18
+ }
19
+ const srcDir = join(resolve(process.cwd(), projectRootRel), 'src');
20
+
21
+ const main = async () => {
22
+ const { accountId, region } = await resolveAwsConfig();
23
+ const env = process.env.TF_ENV ?? 'dev';
24
+ const bucket = `${accountId}-tf-state-${region}`;
25
+ const key = `${STATE_KEY_PREFIX}/${env}/terraform.tfstate`;
26
+
27
+ execFileSync(
28
+ 'terraform',
29
+ [
30
+ 'init',
31
+ '-reconfigure',
32
+ `-backend-config=region=${region}`,
33
+ `-backend-config=bucket=${bucket}`,
34
+ `-backend-config=key=${key}`,
35
+ ...process.argv.slice(3),
36
+ ],
37
+ { cwd: srcDir, stdio: 'inherit' },
38
+ );
39
+ };
40
+
41
+ main().catch((err) => {
42
+ console.error(err);
43
+ process.exit(1);
44
+ });
@@ -53,14 +53,8 @@ async function terraformProjectGenerator(tree, schema) {
53
53
  executor: 'nx:run-commands',
54
54
  options: {
55
55
  forwardAllArgs: true,
56
- commands: [
57
- `aws s3 cp s3://$(aws sts get-caller-identity --query Account --output text)-tf-state-$(aws configure get region)/bootstrap.tfstate ${tfDistDir}/bootstrap.tfstate || true`,
58
- 'terraform init',
59
- `terraform apply -auto-approve -state=${tfDistDir}/bootstrap.tfstate -var="aws_region=$(aws configure get region)"`,
60
- `aws s3 cp ${tfDistDir}/bootstrap.tfstate s3://$(aws sts get-caller-identity --query Account --output text)-tf-state-$(aws configure get region)/bootstrap.tfstate`,
61
- ],
62
- parallel: false,
63
- cwd: '{projectRoot}/bootstrap',
56
+ commands: ['tsx {projectRoot}/scripts/bootstrap.ts {projectRoot}'],
57
+ cwd: '{workspaceRoot}',
64
58
  },
65
59
  },
66
60
  'bootstrap-destroy': {
@@ -96,12 +90,13 @@ async function terraformProjectGenerator(tree, schema) {
96
90
  defaultConfiguration: 'dev',
97
91
  configurations: {
98
92
  dev: {
99
- command: `terraform init -reconfigure -backend-config="region=$(aws configure get region)" -backend-config="bucket=$(aws sts get-caller-identity --query Account --output text)-tf-state-$(aws configure get region)" -backend-config="key=${(0, names_1.kebabCase)(lib.fullyQualifiedName)}/dev/terraform.tfstate"`,
93
+ env: { TF_ENV: 'dev' },
100
94
  },
101
95
  },
102
96
  options: {
103
97
  forwardAllArgs: true,
104
- cwd: '{projectRoot}/src',
98
+ commands: ['tsx {projectRoot}/scripts/init.ts {projectRoot}'],
99
+ cwd: '{workspaceRoot}',
105
100
  },
106
101
  dependsOn: ['^init'],
107
102
  },
@@ -198,6 +193,7 @@ async function terraformProjectGenerator(tree, schema) {
198
193
  lib.dir, // destination path of the files
199
194
  {
200
195
  metricsModulePath,
196
+ stateKeyPrefix: (0, names_1.kebabCase)(lib.fullyQualifiedName),
201
197
  }, {
202
198
  overwriteStrategy: devkit_1.OverwriteStrategy.Overwrite,
203
199
  });
@@ -214,7 +210,16 @@ async function terraformProjectGenerator(tree, schema) {
214
210
  await (0, metrics_1.addGeneratorMetricsIfApplicable)(tree, [
215
211
  exports.TERRAFORM_PROJECT_GENERATOR_INFO,
216
212
  ]);
217
- (0, devkit_1.addDependenciesToPackageJson)(tree, {}, (0, versions_1.withVersions)(['@nx-extend/terraform', 'make-dir-cli']));
213
+ (0, devkit_1.addDependenciesToPackageJson)(tree, {}, (0, versions_1.withVersions)([
214
+ '@nx-extend/terraform',
215
+ 'make-dir-cli',
216
+ 'tsx',
217
+ '@aws-sdk/client-s3',
218
+ '@aws-sdk/client-sts',
219
+ '@aws-sdk/credential-providers',
220
+ '@smithy/config-resolver',
221
+ '@smithy/node-config-provider',
222
+ ]));
218
223
  // @nx-extend/terraform has a peer dependency on @nx/devkit ^21.0.0 which causes
219
224
  // npm install to fail, so for NPM we add a resolution
220
225
  // Can remove when https://github.com/TriPSs/nx-extend/issues/407 is addressed