@aws/nx-plugin 0.101.0 → 0.103.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.
@@ -16,6 +16,7 @@ export * from './workspace.js';
16
16
  exports[`py#mcp-server generator > should match snapshot for BedrockAgentCoreRuntime generated constructs files > mcp-server-construct.ts 1`] = `
17
17
  "import { Lazy, Names } from 'aws-cdk-lib';
18
18
  import { Platform } from 'aws-cdk-lib/aws-ecr-assets';
19
+ import { Connections, IConnectable } from 'aws-cdk-lib/aws-ec2';
19
20
  import { Construct } from 'constructs';
20
21
  import * as path from 'path';
21
22
  import * as url from 'url';
@@ -37,7 +38,10 @@ export type SnapshotBedrockServerProps = Omit<
37
38
  | 'authorizerConfiguration'
38
39
  >;
39
40
 
40
- export class SnapshotBedrockServer extends Construct implements IGrantable {
41
+ export class SnapshotBedrockServer
42
+ extends Construct
43
+ implements IGrantable, IConnectable
44
+ {
41
45
  public readonly dockerImage: AgentRuntimeArtifact;
42
46
  public readonly agentCoreRuntime: Runtime;
43
47
 
@@ -89,6 +93,13 @@ export class SnapshotBedrockServer extends Construct implements IGrantable {
89
93
  return this.agentCoreRuntime.grantPrincipal;
90
94
  }
91
95
 
96
+ /**
97
+ * Network connections for this agent runtime.
98
+ */
99
+ public get connections(): Connections {
100
+ return this.agentCoreRuntime.connections;
101
+ }
102
+
92
103
  /**
93
104
  * Grants IAM permissions to invoke this agent runtime.
94
105
  *
@@ -22,6 +22,7 @@ CMD ["python", "bin/opentelemetry-instrument", "python", "-m", "proj_test_projec
22
22
  exports[`py#strands-agent generator > should match snapshot for BedrockAgentCoreRuntime generated constructs files > agent-construct.ts 1`] = `
23
23
  "import { Lazy, Names } from 'aws-cdk-lib';
24
24
  import { Platform } from 'aws-cdk-lib/aws-ecr-assets';
25
+ import { Connections, IConnectable } from 'aws-cdk-lib/aws-ec2';
25
26
  import { Construct } from 'constructs';
26
27
  import * as path from 'path';
27
28
  import * as url from 'url';
@@ -48,7 +49,10 @@ export type SnapshotBedrockAgentProps = Omit<
48
49
  | 'authorizerConfiguration'
49
50
  >;
50
51
 
51
- export class SnapshotBedrockAgent extends Construct implements IGrantable {
52
+ export class SnapshotBedrockAgent
53
+ extends Construct
54
+ implements IGrantable, IConnectable
55
+ {
52
56
  public readonly dockerImage: AgentRuntimeArtifact;
53
57
  public readonly agentCoreRuntime: Runtime;
54
58
 
@@ -110,6 +114,13 @@ export class SnapshotBedrockAgent extends Construct implements IGrantable {
110
114
  return this.agentCoreRuntime.grantPrincipal;
111
115
  }
112
116
 
117
+ /**
118
+ * Network connections for this agent runtime.
119
+ */
120
+ public get connections(): Connections {
121
+ return this.agentCoreRuntime.connections;
122
+ }
123
+
113
124
  /**
114
125
  * Grants IAM permissions to invoke this agent runtime.
115
126
  *
@@ -85,6 +85,12 @@ export interface TestApiProps<
85
85
  identity: {
86
86
  userPool: IUserPool;
87
87
  };
88
+ /**
89
+ * Whether to enable AWS WAFv2 with the default managed ruleset on the API's default stage.
90
+ *
91
+ * @default true
92
+ */
93
+ enableWaf?: boolean;
88
94
  }
89
95
 
90
96
  /**
@@ -259,6 +265,12 @@ export interface TestApiProps<
259
265
  * Map of operation names to their API Gateway integrations
260
266
  */
261
267
  integrations: TIntegrations;
268
+ /**
269
+ * Whether to enable AWS WAFv2 with the default managed ruleset on the API's default stage.
270
+ *
271
+ * @default true
272
+ */
273
+ enableWaf?: boolean;
262
274
  }
263
275
 
264
276
  /**
@@ -419,6 +431,10 @@ terraform {
419
431
  source = "hashicorp/aws"
420
432
  version = "~> 6.33"
421
433
  }
434
+ random = {
435
+ source = "hashicorp/random"
436
+ version = "~> 3.1"
437
+ }
422
438
  }
423
439
  }
424
440
 
@@ -447,6 +463,12 @@ variable "stage_auto_deploy" {
447
463
  default = true
448
464
  }
449
465
 
466
+ variable "enable_waf" {
467
+ description = "Whether to enable AWS WAFv2 with the default managed ruleset (AWSManagedRulesCommonRuleSet and AWSManagedRulesKnownBadInputsRuleSet). The Web ACL is created here and associated with the stage in the consuming module."
468
+ type = bool
469
+ default = true
470
+ }
471
+
450
472
  # CORS Configuration
451
473
 
452
474
  variable "cors_allow_headers" {
@@ -528,6 +550,106 @@ resource "aws_api_gateway_gateway_response" "cors_5xx" {
528
550
  }
529
551
  }
530
552
 
553
+ resource "aws_wafv2_web_acl" "api_waf" {
554
+ #checkov:skip=CKV2_AWS_31:Logging configuration is defined below in aws_wafv2_web_acl_logging_configuration.api_waf_logging; Checkov does not resolve the separate resource
555
+ count = var.enable_waf ? 1 : 0
556
+
557
+ name = "\${var.api_name}-waf"
558
+ scope = "REGIONAL"
559
+
560
+ default_action {
561
+ allow {}
562
+ }
563
+
564
+ rule {
565
+ name = "CRSRule"
566
+ priority = 0
567
+
568
+ override_action {
569
+ none {}
570
+ }
571
+
572
+ statement {
573
+ managed_rule_group_statement {
574
+ name = "AWSManagedRulesCommonRuleSet"
575
+ vendor_name = "AWS"
576
+
577
+ # Count instead of Block: the CRS 8 KB body limit is too restrictive for most APIs.
578
+ rule_action_override {
579
+ name = "SizeRestrictions_BODY"
580
+ action_to_use {
581
+ count {}
582
+ }
583
+ }
584
+ }
585
+ }
586
+
587
+ visibility_config {
588
+ cloudwatch_metrics_enabled = true
589
+ metric_name = "\${var.api_name}WebAcl-CRS"
590
+ sampled_requests_enabled = true
591
+ }
592
+ }
593
+
594
+ rule {
595
+ name = "KnownBadInputsRule"
596
+ priority = 1
597
+
598
+ override_action {
599
+ none {}
600
+ }
601
+
602
+ statement {
603
+ managed_rule_group_statement {
604
+ name = "AWSManagedRulesKnownBadInputsRuleSet"
605
+ vendor_name = "AWS"
606
+ }
607
+ }
608
+
609
+ visibility_config {
610
+ cloudwatch_metrics_enabled = true
611
+ metric_name = "\${var.api_name}WebAcl-KnownBadInputs"
612
+ sampled_requests_enabled = true
613
+ }
614
+ }
615
+
616
+ visibility_config {
617
+ cloudwatch_metrics_enabled = true
618
+ metric_name = "\${var.api_name}WebAcl"
619
+ sampled_requests_enabled = true
620
+ }
621
+
622
+ tags = var.tags
623
+
624
+ lifecycle {
625
+ create_before_destroy = true
626
+ }
627
+ }
628
+
629
+ # CloudWatch Log Group for WAF request logs. Name must start with \`aws-waf-logs-\`.
630
+ resource "aws_cloudwatch_log_group" "api_waf_logs" {
631
+ #checkov:skip=CKV_AWS_158:Using default CloudWatch log encryption
632
+ #checkov:skip=CKV_AWS_338:Log retention set to one month which is sufficient for WAF logs
633
+ count = var.enable_waf ? 1 : 0
634
+
635
+ name = "aws-waf-logs-\${var.api_name}-\${random_id.waf_log_suffix[0].hex}"
636
+ retention_in_days = 30
637
+ tags = var.tags
638
+ }
639
+
640
+ resource "random_id" "waf_log_suffix" {
641
+ count = var.enable_waf ? 1 : 0
642
+ byte_length = 4
643
+ }
644
+
645
+ resource "aws_wafv2_web_acl_logging_configuration" "api_waf_logging" {
646
+ count = var.enable_waf ? 1 : 0
647
+
648
+ log_destination_configs = [aws_cloudwatch_log_group.api_waf_logs[0].arn]
649
+ resource_arn = aws_wafv2_web_acl.api_waf[0].arn
650
+ }
651
+
652
+
531
653
  # Outputs
532
654
 
533
655
  output "api_id" {
@@ -555,6 +677,11 @@ output "api_root_resource_id" {
555
677
  value = aws_api_gateway_rest_api.rest_api.root_resource_id
556
678
  }
557
679
 
680
+ output "waf_web_acl_arn" {
681
+ description = "ARN of the WAFv2 Web ACL associated with the API, or null if WAF is disabled"
682
+ value = var.enable_waf ? aws_wafv2_web_acl.api_waf[0].arn : null
683
+ }
684
+
558
685
  # Note: Stage and deployment outputs are provided by the consuming module
559
686
 
560
687
  # Note: CloudWatch log group outputs removed due to account-level CloudWatch Logs role ARN requirement"
@@ -609,6 +736,13 @@ variable "cors_allow_origins" {
609
736
  default = ["*"]
610
737
  }
611
738
 
739
+ # WAF Configuration
740
+ variable "enable_waf" {
741
+ description = "Whether to enable AWS WAFv2 with the default managed ruleset on the API stage"
742
+ type = bool
743
+ default = true
744
+ }
745
+
612
746
  # Tags
613
747
  variable "tags" {
614
748
  description = "Tags to apply to all resources"
@@ -638,6 +772,9 @@ module "rest_api" {
638
772
  stage_name = "prod"
639
773
  stage_auto_deploy = true
640
774
 
775
+ # WAF Configuration
776
+ enable_waf = var.enable_waf
777
+
641
778
  # CORS Configuration
642
779
  cors_allow_headers = var.cors_allow_headers
643
780
  cors_allow_methods = var.cors_allow_methods
@@ -647,6 +784,15 @@ module "rest_api" {
647
784
  tags = var.tags
648
785
  }
649
786
 
787
+ resource "aws_wafv2_web_acl_association" "api_waf_association" {
788
+ count = var.enable_waf ? 1 : 0
789
+
790
+ resource_arn = aws_api_gateway_stage.api_stage.arn
791
+ web_acl_arn = module.rest_api.waf_web_acl_arn
792
+
793
+ depends_on = [aws_api_gateway_stage.api_stage]
794
+ }
795
+
650
796
  # Lambda function
651
797
  resource "aws_lambda_function" "api_lambda" {
652
798
  #checkov:skip=CKV_AWS_117:Lambda function does not need to be in VPC for this use case
@@ -855,6 +1001,7 @@ resource "aws_api_gateway_stage" "api_stage" {
855
1001
  #checkov:skip=CKV_AWS_76:API Gateway access logging disabled due to account-level CloudWatch Logs role ARN requirement
856
1002
  #checkov:skip=CKV2_AWS_4:API Gateway logging level not applicable as access logging is disabled
857
1003
  #checkov:skip=CKV2_AWS_51:Client certificate authentication not required for this use case
1004
+ #checkov:skip=CKV2_AWS_77:WAFv2 Web ACL is attached via aws_wafv2_web_acl_association when var.enable_waf is true (default) and includes AWSManagedRulesKnownBadInputsRuleSet for Log4j protection; Checkov does not track the association across modules
858
1005
  deployment_id = aws_api_gateway_deployment.api_deployment.id
859
1006
  rest_api_id = module.rest_api.api_id
860
1007
  stage_name = "prod"
@@ -1446,6 +1593,10 @@ terraform {
1446
1593
  source = "hashicorp/aws"
1447
1594
  version = "~> 6.33"
1448
1595
  }
1596
+ random = {
1597
+ source = "hashicorp/random"
1598
+ version = "~> 3.1"
1599
+ }
1449
1600
  }
1450
1601
  }
1451
1602
 
@@ -1474,6 +1625,12 @@ variable "stage_auto_deploy" {
1474
1625
  default = true
1475
1626
  }
1476
1627
 
1628
+ variable "enable_waf" {
1629
+ description = "Whether to enable AWS WAFv2 with the default managed ruleset (AWSManagedRulesCommonRuleSet and AWSManagedRulesKnownBadInputsRuleSet). The Web ACL is created here and associated with the stage in the consuming module."
1630
+ type = bool
1631
+ default = true
1632
+ }
1633
+
1477
1634
  # CORS Configuration
1478
1635
 
1479
1636
  variable "cors_allow_headers" {
@@ -1555,6 +1712,106 @@ resource "aws_api_gateway_gateway_response" "cors_5xx" {
1555
1712
  }
1556
1713
  }
1557
1714
 
1715
+ resource "aws_wafv2_web_acl" "api_waf" {
1716
+ #checkov:skip=CKV2_AWS_31:Logging configuration is defined below in aws_wafv2_web_acl_logging_configuration.api_waf_logging; Checkov does not resolve the separate resource
1717
+ count = var.enable_waf ? 1 : 0
1718
+
1719
+ name = "\${var.api_name}-waf"
1720
+ scope = "REGIONAL"
1721
+
1722
+ default_action {
1723
+ allow {}
1724
+ }
1725
+
1726
+ rule {
1727
+ name = "CRSRule"
1728
+ priority = 0
1729
+
1730
+ override_action {
1731
+ none {}
1732
+ }
1733
+
1734
+ statement {
1735
+ managed_rule_group_statement {
1736
+ name = "AWSManagedRulesCommonRuleSet"
1737
+ vendor_name = "AWS"
1738
+
1739
+ # Count instead of Block: the CRS 8 KB body limit is too restrictive for most APIs.
1740
+ rule_action_override {
1741
+ name = "SizeRestrictions_BODY"
1742
+ action_to_use {
1743
+ count {}
1744
+ }
1745
+ }
1746
+ }
1747
+ }
1748
+
1749
+ visibility_config {
1750
+ cloudwatch_metrics_enabled = true
1751
+ metric_name = "\${var.api_name}WebAcl-CRS"
1752
+ sampled_requests_enabled = true
1753
+ }
1754
+ }
1755
+
1756
+ rule {
1757
+ name = "KnownBadInputsRule"
1758
+ priority = 1
1759
+
1760
+ override_action {
1761
+ none {}
1762
+ }
1763
+
1764
+ statement {
1765
+ managed_rule_group_statement {
1766
+ name = "AWSManagedRulesKnownBadInputsRuleSet"
1767
+ vendor_name = "AWS"
1768
+ }
1769
+ }
1770
+
1771
+ visibility_config {
1772
+ cloudwatch_metrics_enabled = true
1773
+ metric_name = "\${var.api_name}WebAcl-KnownBadInputs"
1774
+ sampled_requests_enabled = true
1775
+ }
1776
+ }
1777
+
1778
+ visibility_config {
1779
+ cloudwatch_metrics_enabled = true
1780
+ metric_name = "\${var.api_name}WebAcl"
1781
+ sampled_requests_enabled = true
1782
+ }
1783
+
1784
+ tags = var.tags
1785
+
1786
+ lifecycle {
1787
+ create_before_destroy = true
1788
+ }
1789
+ }
1790
+
1791
+ # CloudWatch Log Group for WAF request logs. Name must start with \`aws-waf-logs-\`.
1792
+ resource "aws_cloudwatch_log_group" "api_waf_logs" {
1793
+ #checkov:skip=CKV_AWS_158:Using default CloudWatch log encryption
1794
+ #checkov:skip=CKV_AWS_338:Log retention set to one month which is sufficient for WAF logs
1795
+ count = var.enable_waf ? 1 : 0
1796
+
1797
+ name = "aws-waf-logs-\${var.api_name}-\${random_id.waf_log_suffix[0].hex}"
1798
+ retention_in_days = 30
1799
+ tags = var.tags
1800
+ }
1801
+
1802
+ resource "random_id" "waf_log_suffix" {
1803
+ count = var.enable_waf ? 1 : 0
1804
+ byte_length = 4
1805
+ }
1806
+
1807
+ resource "aws_wafv2_web_acl_logging_configuration" "api_waf_logging" {
1808
+ count = var.enable_waf ? 1 : 0
1809
+
1810
+ log_destination_configs = [aws_cloudwatch_log_group.api_waf_logs[0].arn]
1811
+ resource_arn = aws_wafv2_web_acl.api_waf[0].arn
1812
+ }
1813
+
1814
+
1558
1815
  # Outputs
1559
1816
 
1560
1817
  output "api_id" {
@@ -1582,6 +1839,11 @@ output "api_root_resource_id" {
1582
1839
  value = aws_api_gateway_rest_api.rest_api.root_resource_id
1583
1840
  }
1584
1841
 
1842
+ output "waf_web_acl_arn" {
1843
+ description = "ARN of the WAFv2 Web ACL associated with the API, or null if WAF is disabled"
1844
+ value = var.enable_waf ? aws_wafv2_web_acl.api_waf[0].arn : null
1845
+ }
1846
+
1585
1847
  # Note: Stage and deployment outputs are provided by the consuming module
1586
1848
 
1587
1849
  # Note: CloudWatch log group outputs removed due to account-level CloudWatch Logs role ARN requirement",
@@ -1643,6 +1905,13 @@ variable "cors_allow_origins" {
1643
1905
  default = ["*"]
1644
1906
  }
1645
1907
 
1908
+ # WAF Configuration
1909
+ variable "enable_waf" {
1910
+ description = "Whether to enable AWS WAFv2 with the default managed ruleset on the API stage"
1911
+ type = bool
1912
+ default = true
1913
+ }
1914
+
1646
1915
  # Tags
1647
1916
  variable "tags" {
1648
1917
  description = "Tags to apply to all resources"
@@ -1672,6 +1941,9 @@ module "rest_api" {
1672
1941
  stage_name = "prod"
1673
1942
  stage_auto_deploy = true
1674
1943
 
1944
+ # WAF Configuration
1945
+ enable_waf = var.enable_waf
1946
+
1675
1947
  # CORS Configuration
1676
1948
  cors_allow_headers = var.cors_allow_headers
1677
1949
  cors_allow_methods = var.cors_allow_methods
@@ -1681,6 +1953,15 @@ module "rest_api" {
1681
1953
  tags = var.tags
1682
1954
  }
1683
1955
 
1956
+ resource "aws_wafv2_web_acl_association" "api_waf_association" {
1957
+ count = var.enable_waf ? 1 : 0
1958
+
1959
+ resource_arn = aws_api_gateway_stage.api_stage.arn
1960
+ web_acl_arn = module.rest_api.waf_web_acl_arn
1961
+
1962
+ depends_on = [aws_api_gateway_stage.api_stage]
1963
+ }
1964
+
1684
1965
  # Lambda function
1685
1966
  resource "aws_lambda_function" "api_lambda" {
1686
1967
  #checkov:skip=CKV_AWS_117:Lambda function does not need to be in VPC for this use case
@@ -1896,6 +2177,7 @@ resource "aws_api_gateway_stage" "api_stage" {
1896
2177
  #checkov:skip=CKV_AWS_76:API Gateway access logging disabled due to account-level CloudWatch Logs role ARN requirement
1897
2178
  #checkov:skip=CKV2_AWS_4:API Gateway logging level not applicable as access logging is disabled
1898
2179
  #checkov:skip=CKV2_AWS_51:Client certificate authentication not required for this use case
2180
+ #checkov:skip=CKV2_AWS_77:WAFv2 Web ACL is attached via aws_wafv2_web_acl_association when var.enable_waf is true (default) and includes AWSManagedRulesKnownBadInputsRuleSet for Log4j protection; Checkov does not track the association across modules
1899
2181
  deployment_id = aws_api_gateway_deployment.api_deployment.id
1900
2182
  rest_api_id = module.rest_api.api_id
1901
2183
  stage_name = "prod"
@@ -2093,6 +2375,10 @@ terraform {
2093
2375
  source = "hashicorp/aws"
2094
2376
  version = "~> 6.33"
2095
2377
  }
2378
+ random = {
2379
+ source = "hashicorp/random"
2380
+ version = "~> 3.1"
2381
+ }
2096
2382
  }
2097
2383
  }
2098
2384
 
@@ -2121,6 +2407,12 @@ variable "stage_auto_deploy" {
2121
2407
  default = true
2122
2408
  }
2123
2409
 
2410
+ variable "enable_waf" {
2411
+ description = "Whether to enable AWS WAFv2 with the default managed ruleset (AWSManagedRulesCommonRuleSet and AWSManagedRulesKnownBadInputsRuleSet). The Web ACL is created here and associated with the stage in the consuming module."
2412
+ type = bool
2413
+ default = true
2414
+ }
2415
+
2124
2416
  # CORS Configuration
2125
2417
 
2126
2418
  variable "cors_allow_headers" {
@@ -2202,6 +2494,106 @@ resource "aws_api_gateway_gateway_response" "cors_5xx" {
2202
2494
  }
2203
2495
  }
2204
2496
 
2497
+ resource "aws_wafv2_web_acl" "api_waf" {
2498
+ #checkov:skip=CKV2_AWS_31:Logging configuration is defined below in aws_wafv2_web_acl_logging_configuration.api_waf_logging; Checkov does not resolve the separate resource
2499
+ count = var.enable_waf ? 1 : 0
2500
+
2501
+ name = "\${var.api_name}-waf"
2502
+ scope = "REGIONAL"
2503
+
2504
+ default_action {
2505
+ allow {}
2506
+ }
2507
+
2508
+ rule {
2509
+ name = "CRSRule"
2510
+ priority = 0
2511
+
2512
+ override_action {
2513
+ none {}
2514
+ }
2515
+
2516
+ statement {
2517
+ managed_rule_group_statement {
2518
+ name = "AWSManagedRulesCommonRuleSet"
2519
+ vendor_name = "AWS"
2520
+
2521
+ # Count instead of Block: the CRS 8 KB body limit is too restrictive for most APIs.
2522
+ rule_action_override {
2523
+ name = "SizeRestrictions_BODY"
2524
+ action_to_use {
2525
+ count {}
2526
+ }
2527
+ }
2528
+ }
2529
+ }
2530
+
2531
+ visibility_config {
2532
+ cloudwatch_metrics_enabled = true
2533
+ metric_name = "\${var.api_name}WebAcl-CRS"
2534
+ sampled_requests_enabled = true
2535
+ }
2536
+ }
2537
+
2538
+ rule {
2539
+ name = "KnownBadInputsRule"
2540
+ priority = 1
2541
+
2542
+ override_action {
2543
+ none {}
2544
+ }
2545
+
2546
+ statement {
2547
+ managed_rule_group_statement {
2548
+ name = "AWSManagedRulesKnownBadInputsRuleSet"
2549
+ vendor_name = "AWS"
2550
+ }
2551
+ }
2552
+
2553
+ visibility_config {
2554
+ cloudwatch_metrics_enabled = true
2555
+ metric_name = "\${var.api_name}WebAcl-KnownBadInputs"
2556
+ sampled_requests_enabled = true
2557
+ }
2558
+ }
2559
+
2560
+ visibility_config {
2561
+ cloudwatch_metrics_enabled = true
2562
+ metric_name = "\${var.api_name}WebAcl"
2563
+ sampled_requests_enabled = true
2564
+ }
2565
+
2566
+ tags = var.tags
2567
+
2568
+ lifecycle {
2569
+ create_before_destroy = true
2570
+ }
2571
+ }
2572
+
2573
+ # CloudWatch Log Group for WAF request logs. Name must start with \`aws-waf-logs-\`.
2574
+ resource "aws_cloudwatch_log_group" "api_waf_logs" {
2575
+ #checkov:skip=CKV_AWS_158:Using default CloudWatch log encryption
2576
+ #checkov:skip=CKV_AWS_338:Log retention set to one month which is sufficient for WAF logs
2577
+ count = var.enable_waf ? 1 : 0
2578
+
2579
+ name = "aws-waf-logs-\${var.api_name}-\${random_id.waf_log_suffix[0].hex}"
2580
+ retention_in_days = 30
2581
+ tags = var.tags
2582
+ }
2583
+
2584
+ resource "random_id" "waf_log_suffix" {
2585
+ count = var.enable_waf ? 1 : 0
2586
+ byte_length = 4
2587
+ }
2588
+
2589
+ resource "aws_wafv2_web_acl_logging_configuration" "api_waf_logging" {
2590
+ count = var.enable_waf ? 1 : 0
2591
+
2592
+ log_destination_configs = [aws_cloudwatch_log_group.api_waf_logs[0].arn]
2593
+ resource_arn = aws_wafv2_web_acl.api_waf[0].arn
2594
+ }
2595
+
2596
+
2205
2597
  # Outputs
2206
2598
 
2207
2599
  output "api_id" {
@@ -2229,6 +2621,11 @@ output "api_root_resource_id" {
2229
2621
  value = aws_api_gateway_rest_api.rest_api.root_resource_id
2230
2622
  }
2231
2623
 
2624
+ output "waf_web_acl_arn" {
2625
+ description = "ARN of the WAFv2 Web ACL associated with the API, or null if WAF is disabled"
2626
+ value = var.enable_waf ? aws_wafv2_web_acl.api_waf[0].arn : null
2627
+ }
2628
+
2232
2629
  # Note: Stage and deployment outputs are provided by the consuming module
2233
2630
 
2234
2631
  # Note: CloudWatch log group outputs removed due to account-level CloudWatch Logs role ARN requirement",
@@ -2280,6 +2677,13 @@ variable "cors_allow_origins" {
2280
2677
  default = ["*"]
2281
2678
  }
2282
2679
 
2680
+ # WAF Configuration
2681
+ variable "enable_waf" {
2682
+ description = "Whether to enable AWS WAFv2 with the default managed ruleset on the API stage"
2683
+ type = bool
2684
+ default = true
2685
+ }
2686
+
2283
2687
  # Tags
2284
2688
  variable "tags" {
2285
2689
  description = "Tags to apply to all resources"
@@ -2309,6 +2713,9 @@ module "rest_api" {
2309
2713
  stage_name = "prod"
2310
2714
  stage_auto_deploy = true
2311
2715
 
2716
+ # WAF Configuration
2717
+ enable_waf = var.enable_waf
2718
+
2312
2719
  # CORS Configuration
2313
2720
  cors_allow_headers = var.cors_allow_headers
2314
2721
  cors_allow_methods = var.cors_allow_methods
@@ -2318,6 +2725,15 @@ module "rest_api" {
2318
2725
  tags = var.tags
2319
2726
  }
2320
2727
 
2728
+ resource "aws_wafv2_web_acl_association" "api_waf_association" {
2729
+ count = var.enable_waf ? 1 : 0
2730
+
2731
+ resource_arn = aws_api_gateway_stage.api_stage.arn
2732
+ web_acl_arn = module.rest_api.waf_web_acl_arn
2733
+
2734
+ depends_on = [aws_api_gateway_stage.api_stage]
2735
+ }
2736
+
2321
2737
  # Lambda function
2322
2738
  resource "aws_lambda_function" "api_lambda" {
2323
2739
  #checkov:skip=CKV_AWS_117:Lambda function does not need to be in VPC for this use case
@@ -2524,6 +2940,7 @@ resource "aws_api_gateway_stage" "api_stage" {
2524
2940
  #checkov:skip=CKV_AWS_76:API Gateway access logging disabled due to account-level CloudWatch Logs role ARN requirement
2525
2941
  #checkov:skip=CKV2_AWS_4:API Gateway logging level not applicable as access logging is disabled
2526
2942
  #checkov:skip=CKV2_AWS_51:Client certificate authentication not required for this use case
2943
+ #checkov:skip=CKV2_AWS_77:WAFv2 Web ACL is attached via aws_wafv2_web_acl_association when var.enable_waf is true (default) and includes AWSManagedRulesKnownBadInputsRuleSet for Log4j protection; Checkov does not track the association across modules
2527
2944
  deployment_id = aws_api_gateway_deployment.api_deployment.id
2528
2945
  rest_api_id = module.rest_api.api_id
2529
2946
  stage_name = "prod"
@@ -2768,6 +3185,13 @@ variable "cors_allow_origins" {
2768
3185
  default = ["*"]
2769
3186
  }
2770
3187
 
3188
+ # WAF Configuration
3189
+ variable "enable_waf" {
3190
+ description = "Whether to enable AWS WAFv2 with the default managed ruleset on the API stage"
3191
+ type = bool
3192
+ default = true
3193
+ }
3194
+
2771
3195
  # Tags
2772
3196
  variable "tags" {
2773
3197
  description = "Tags to apply to all resources"
@@ -2797,6 +3221,9 @@ module "rest_api" {
2797
3221
  stage_name = "prod"
2798
3222
  stage_auto_deploy = true
2799
3223
 
3224
+ # WAF Configuration
3225
+ enable_waf = var.enable_waf
3226
+
2800
3227
  # CORS Configuration
2801
3228
  cors_allow_headers = var.cors_allow_headers
2802
3229
  cors_allow_methods = var.cors_allow_methods
@@ -2806,6 +3233,15 @@ module "rest_api" {
2806
3233
  tags = var.tags
2807
3234
  }
2808
3235
 
3236
+ resource "aws_wafv2_web_acl_association" "api_waf_association" {
3237
+ count = var.enable_waf ? 1 : 0
3238
+
3239
+ resource_arn = aws_api_gateway_stage.api_stage.arn
3240
+ web_acl_arn = module.rest_api.waf_web_acl_arn
3241
+
3242
+ depends_on = [aws_api_gateway_stage.api_stage]
3243
+ }
3244
+
2809
3245
  # Lambda function
2810
3246
  resource "aws_lambda_function" "api_lambda" {
2811
3247
  #checkov:skip=CKV_AWS_117:Lambda function does not need to be in VPC for this use case
@@ -3014,6 +3450,7 @@ resource "aws_api_gateway_stage" "api_stage" {
3014
3450
  #checkov:skip=CKV_AWS_76:API Gateway access logging disabled due to account-level CloudWatch Logs role ARN requirement
3015
3451
  #checkov:skip=CKV2_AWS_4:API Gateway logging level not applicable as access logging is disabled
3016
3452
  #checkov:skip=CKV2_AWS_51:Client certificate authentication not required for this use case
3453
+ #checkov:skip=CKV2_AWS_77:WAFv2 Web ACL is attached via aws_wafv2_web_acl_association when var.enable_waf is true (default) and includes AWSManagedRulesKnownBadInputsRuleSet for Log4j protection; Checkov does not track the association across modules
3017
3454
  deployment_id = aws_api_gateway_deployment.api_deployment.id
3018
3455
  rest_api_id = module.rest_api.api_id
3019
3456
  stage_name = "prod"