@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.
- package/package.json +2 -2
- package/src/py/fast-api/__snapshots__/generator.spec.ts.snap +167 -89
- package/src/py/lambda-function/__snapshots__/generator.spec.ts.snap +72 -16
- package/src/py/mcp-server/__snapshots__/generator.spec.ts.snap +11 -9
- package/src/py/strands-agent/__snapshots__/generator.spec.ts.snap +11 -9
- package/src/py/strands-agent/generator.js +95 -2
- package/src/py/strands-agent/generator.js.map +1 -1
- package/src/py/strands-agent/scripts/http/chat.ts.template +38 -0
- package/src/smithy/ts/api/__snapshots__/generator.spec.ts.snap +113 -61
- package/src/terraform/project/files/application/scripts/aws-config.ts.template +41 -0
- package/src/terraform/project/files/application/scripts/bootstrap.ts.template +91 -0
- package/src/terraform/project/files/application/scripts/init.ts.template +44 -0
- package/src/terraform/project/generator.js +16 -11
- package/src/terraform/project/generator.js.map +1 -1
- package/src/trpc/backend/__snapshots__/generator.spec.ts.snap +167 -89
- package/src/ts/lambda-function/__snapshots__/generator.spec.ts.snap +72 -16
- package/src/ts/mcp-server/__snapshots__/generator.spec.ts.snap +11 -9
- package/src/ts/react-website/app/__snapshots__/generator.spec.ts.snap +7 -0
- package/src/ts/strands-agent/__snapshots__/generator.spec.ts.snap +11 -9
- package/src/ts/strands-agent/generator.js +31 -0
- package/src/ts/strands-agent/generator.js.map +1 -1
- package/src/ts/strands-agent/scripts/http/chat.ts.template +35 -0
- package/src/utils/agent-core-constructs/files/terraform/app/agent-core/__nameKebabCase__/__nameKebabCase__.tf.template +14 -12
- package/src/utils/api-constructs/files/terraform/app/apis/http/__apiNameKebabCase__/__apiNameKebabCase__.tf.template +28 -15
- package/src/utils/api-constructs/files/terraform/app/apis/rest/__apiNameKebabCase__/__apiNameKebabCase__.tf.template +29 -16
- package/src/utils/files/terraform/scripts/reset-runtime-config.ts.template +25 -0
- package/src/utils/files/terraform/src/core/asset-bucket/asset-bucket.tf.template +203 -0
- package/src/utils/files/terraform/src/core/runtime-config/appconfig/appconfig.tf.template +42 -39
- package/src/utils/files/terraform/src/core/runtime-config/appconfig-deployment/appconfig-deployment.tf.template +138 -0
- package/src/utils/files/terraform/src/core/runtime-config/entry/entry.tf.template +26 -90
- package/src/utils/files/terraform/src/core/runtime-config/read/read.tf.template +61 -5
- package/src/utils/function-constructs/files/terraform/app/lambda-functions/__functionNameKebabCase__/__functionNameKebabCase__.tf.template +35 -7
- package/src/utils/shared-constructs.js +4 -2
- package/src/utils/shared-constructs.js.map +1 -1
- package/src/utils/versions.d.ts +7 -3
- package/src/utils/versions.js +6 -2
- package/src/utils/versions.js.map +1 -1
- 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
|
|
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
|
-
|
|
804
|
-
|
|
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
|
|
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
|
-
|
|
1985
|
-
|
|
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
|
|
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
|
-
|
|
2757
|
-
|
|
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
|
|
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
|
-
|
|
3265
|
-
|
|
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
|
-
|
|
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
|
-
|
|
93
|
+
env: { TF_ENV: 'dev' },
|
|
100
94
|
},
|
|
101
95
|
},
|
|
102
96
|
options: {
|
|
103
97
|
forwardAllArgs: true,
|
|
104
|
-
|
|
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)([
|
|
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
|