cdk-factory 0.16.15__py3-none-any.whl → 0.17.0__py3-none-any.whl
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.
- cdk_factory/configurations/base_config.py +23 -24
- cdk_factory/configurations/cdk_config.py +1 -1
- cdk_factory/configurations/devops.py +1 -1
- cdk_factory/configurations/resources/cloudfront.py +7 -2
- cdk_factory/configurations/resources/ecr.py +1 -1
- cdk_factory/configurations/resources/ecs_cluster.py +7 -5
- cdk_factory/configurations/resources/ecs_service.py +7 -2
- cdk_factory/configurations/resources/load_balancer.py +8 -9
- cdk_factory/configurations/resources/monitoring.py +8 -3
- cdk_factory/configurations/resources/rds.py +7 -8
- cdk_factory/configurations/resources/rum.py +7 -2
- cdk_factory/configurations/resources/s3.py +1 -1
- cdk_factory/configurations/resources/security_group_full_stack.py +7 -8
- cdk_factory/configurations/resources/vpc.py +19 -0
- cdk_factory/configurations/workload.py +32 -2
- cdk_factory/constructs/ecr/ecr_construct.py +9 -2
- cdk_factory/constructs/lambdas/policies/policy_docs.py +4 -4
- cdk_factory/interfaces/istack.py +4 -4
- cdk_factory/interfaces/networked_stack_mixin.py +6 -6
- cdk_factory/interfaces/standardized_ssm_mixin.py +612 -0
- cdk_factory/interfaces/vpc_provider_mixin.py +53 -29
- cdk_factory/lambdas/edge/ip_gate/handler.py +42 -40
- cdk_factory/pipeline/pipeline_factory.py +3 -3
- cdk_factory/stack_library/__init__.py +3 -2
- cdk_factory/stack_library/acm/acm_stack.py +2 -2
- cdk_factory/stack_library/api_gateway/api_gateway_stack.py +84 -59
- cdk_factory/stack_library/auto_scaling/auto_scaling_stack_standardized.py +530 -0
- cdk_factory/stack_library/code_artifact/code_artifact_stack.py +2 -2
- cdk_factory/stack_library/cognito/cognito_stack.py +152 -92
- cdk_factory/stack_library/dynamodb/dynamodb_stack.py +19 -15
- cdk_factory/stack_library/ecr/ecr_stack.py +2 -2
- cdk_factory/stack_library/ecs/__init__.py +1 -1
- cdk_factory/stack_library/ecs/ecs_cluster_stack_standardized.py +305 -0
- cdk_factory/stack_library/ecs/ecs_service_stack.py +10 -26
- cdk_factory/stack_library/lambda_edge/lambda_edge_stack.py +2 -2
- cdk_factory/stack_library/load_balancer/load_balancer_stack.py +11 -35
- cdk_factory/stack_library/rds/rds_stack.py +10 -27
- cdk_factory/stack_library/route53/route53_stack.py +2 -2
- cdk_factory/stack_library/rum/rum_stack.py +102 -91
- cdk_factory/stack_library/security_group/security_group_full_stack.py +9 -22
- cdk_factory/stack_library/security_group/security_group_stack.py +11 -11
- cdk_factory/stack_library/vpc/vpc_stack_standardized.py +411 -0
- cdk_factory/utilities/api_gateway_integration_utility.py +24 -16
- cdk_factory/utilities/environment_services.py +3 -3
- cdk_factory/utilities/json_loading_utility.py +1 -1
- cdk_factory/validation/config_validator.py +483 -0
- cdk_factory/version.py +1 -1
- {cdk_factory-0.16.15.dist-info → cdk_factory-0.17.0.dist-info}/METADATA +1 -1
- {cdk_factory-0.16.15.dist-info → cdk_factory-0.17.0.dist-info}/RECORD +52 -52
- cdk_factory/interfaces/enhanced_ssm_parameter_mixin.py +0 -321
- cdk_factory/interfaces/ssm_parameter_mixin.py +0 -454
- cdk_factory/stack_library/auto_scaling/auto_scaling_stack.py +0 -721
- cdk_factory/stack_library/ecs/ecs_cluster_stack.py +0 -232
- cdk_factory/stack_library/vpc/vpc_stack.py +0 -298
- {cdk_factory-0.16.15.dist-info → cdk_factory-0.17.0.dist-info}/WHEEL +0 -0
- {cdk_factory-0.16.15.dist-info → cdk_factory-0.17.0.dist-info}/entry_points.txt +0 -0
- {cdk_factory-0.16.15.dist-info → cdk_factory-0.17.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,411 @@
|
|
|
1
|
+
"""
|
|
2
|
+
VPC Stack Pattern for CDK-Factory (Standardized SSM Version)
|
|
3
|
+
Maintainers: Eric Wilson
|
|
4
|
+
MIT License. See Project Root for the license information.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from typing import Dict, Any, List, Optional
|
|
8
|
+
|
|
9
|
+
import aws_cdk as cdk
|
|
10
|
+
from aws_cdk import aws_ec2 as ec2
|
|
11
|
+
from aws_lambda_powertools import Logger
|
|
12
|
+
from constructs import Construct
|
|
13
|
+
|
|
14
|
+
from cdk_factory.configurations.deployment import DeploymentConfig
|
|
15
|
+
from cdk_factory.configurations.stack import StackConfig
|
|
16
|
+
from cdk_factory.configurations.resources.vpc import VpcConfig
|
|
17
|
+
from cdk_factory.interfaces.istack import IStack
|
|
18
|
+
from cdk_factory.interfaces.standardized_ssm_mixin import StandardizedSsmMixin
|
|
19
|
+
from cdk_factory.stack.stack_module_registry import register_stack
|
|
20
|
+
from cdk_factory.workload.workload_factory import WorkloadConfig
|
|
21
|
+
|
|
22
|
+
logger = Logger(service="VpcStackStandardized")
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@register_stack("vpc_library_module")
|
|
26
|
+
@register_stack("vpc_stack")
|
|
27
|
+
class VpcStack(IStack, StandardizedSsmMixin):
|
|
28
|
+
"""
|
|
29
|
+
Reusable stack for AWS VPC with standardized SSM integration.
|
|
30
|
+
|
|
31
|
+
This version uses the StandardizedSsmMixin to provide consistent SSM parameter
|
|
32
|
+
handling across all CDK Factory modules.
|
|
33
|
+
|
|
34
|
+
Key Features:
|
|
35
|
+
- Standardized SSM import/export patterns
|
|
36
|
+
- Template variable resolution
|
|
37
|
+
- Comprehensive validation
|
|
38
|
+
- Clear error handling
|
|
39
|
+
- Backward compatibility
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
def __init__(self, scope: Construct, id: str, **kwargs) -> None:
|
|
43
|
+
# Initialize parent classes properly
|
|
44
|
+
super().__init__(scope, id, **kwargs)
|
|
45
|
+
|
|
46
|
+
# Initialize module attributes
|
|
47
|
+
self.vpc_config = None
|
|
48
|
+
self.stack_config = None
|
|
49
|
+
self.deployment = None
|
|
50
|
+
self.workload = None
|
|
51
|
+
self.vpc = None
|
|
52
|
+
|
|
53
|
+
def build(
|
|
54
|
+
self,
|
|
55
|
+
stack_config: StackConfig,
|
|
56
|
+
deployment: DeploymentConfig,
|
|
57
|
+
workload: WorkloadConfig,
|
|
58
|
+
) -> None:
|
|
59
|
+
"""Build the VPC stack"""
|
|
60
|
+
self._build(stack_config, deployment, workload)
|
|
61
|
+
|
|
62
|
+
def _build(
|
|
63
|
+
self,
|
|
64
|
+
stack_config: StackConfig,
|
|
65
|
+
deployment: DeploymentConfig,
|
|
66
|
+
workload: WorkloadConfig,
|
|
67
|
+
) -> None:
|
|
68
|
+
"""Internal build method for the VPC stack"""
|
|
69
|
+
self.stack_config = stack_config
|
|
70
|
+
self.deployment = deployment
|
|
71
|
+
self.workload = workload
|
|
72
|
+
|
|
73
|
+
self.vpc_config = VpcConfig(stack_config.dictionary.get("vpc", {}), deployment)
|
|
74
|
+
vpc_name = deployment.build_resource_name(self.vpc_config.name)
|
|
75
|
+
|
|
76
|
+
# Setup standardized SSM integration
|
|
77
|
+
self.setup_standardized_ssm_integration(
|
|
78
|
+
scope=self,
|
|
79
|
+
config=self.vpc_config,
|
|
80
|
+
resource_type="vpc",
|
|
81
|
+
resource_name=vpc_name,
|
|
82
|
+
deployment=deployment,
|
|
83
|
+
workload=workload
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
# Process SSM imports using standardized method
|
|
87
|
+
self.process_standardized_ssm_imports()
|
|
88
|
+
|
|
89
|
+
# Import any required resources from SSM
|
|
90
|
+
imported_resources = self.get_all_ssm_imports()
|
|
91
|
+
|
|
92
|
+
if imported_resources:
|
|
93
|
+
logger.info(f"Imported resources from SSM: {list(imported_resources.keys())}")
|
|
94
|
+
|
|
95
|
+
# Create the VPC
|
|
96
|
+
self.vpc = self._create_vpc(vpc_name)
|
|
97
|
+
|
|
98
|
+
# Add outputs
|
|
99
|
+
self._add_outputs(vpc_name)
|
|
100
|
+
|
|
101
|
+
# Export SSM parameters
|
|
102
|
+
self._export_ssm_parameters()
|
|
103
|
+
|
|
104
|
+
logger.info(f"VPC {vpc_name} built successfully")
|
|
105
|
+
|
|
106
|
+
def _create_vpc(self, vpc_name: str) -> ec2.Vpc:
|
|
107
|
+
"""Create a VPC with the specified configuration"""
|
|
108
|
+
# Configure subnet configuration
|
|
109
|
+
subnet_configuration = self._get_subnet_configuration()
|
|
110
|
+
|
|
111
|
+
# Configure NAT gateways
|
|
112
|
+
nat_gateway_count = self.vpc_config.nat_gateways.get("count", 1)
|
|
113
|
+
|
|
114
|
+
# Get explicit availability zones to avoid dummy AZs in pipeline synthesis
|
|
115
|
+
# When CDK synthesizes in a pipeline context, it doesn't have access to real AZs
|
|
116
|
+
# So we explicitly specify them based on the deployment region
|
|
117
|
+
availability_zones = None
|
|
118
|
+
if self.deployment:
|
|
119
|
+
region = self.deployment.region or "us-east-1"
|
|
120
|
+
# Explicitly list AZs for the region to avoid dummy values
|
|
121
|
+
max_azs = self.vpc_config.max_azs or 2
|
|
122
|
+
if region == "us-east-1":
|
|
123
|
+
availability_zones = [f"us-east-1{chr(97+i)}" for i in range(max_azs)] # us-east-1a, us-east-1b, etc.
|
|
124
|
+
elif region == "us-east-2":
|
|
125
|
+
availability_zones = [f"us-east-2{chr(97+i)}" for i in range(max_azs)]
|
|
126
|
+
elif region == "us-west-1":
|
|
127
|
+
availability_zones = [f"us-west-1{chr(97+i)}" for i in range(max_azs)]
|
|
128
|
+
elif region == "us-west-2":
|
|
129
|
+
availability_zones = [f"us-west-2{chr(97+i)}" for i in range(max_azs)]
|
|
130
|
+
|
|
131
|
+
# Build VPC properties
|
|
132
|
+
# Note: CDK doesn't allow both 'availability_zones' and 'max_azs' - use one or the other
|
|
133
|
+
vpc_props = {
|
|
134
|
+
"vpc_name": vpc_name,
|
|
135
|
+
"cidr": self.vpc_config.cidr,
|
|
136
|
+
"nat_gateways": nat_gateway_count,
|
|
137
|
+
"subnet_configuration": subnet_configuration,
|
|
138
|
+
"enable_dns_hostnames": self.vpc_config.enable_dns_hostnames,
|
|
139
|
+
"enable_dns_support": self.vpc_config.enable_dns_support,
|
|
140
|
+
"gateway_endpoints": (
|
|
141
|
+
{
|
|
142
|
+
"S3": ec2.GatewayVpcEndpointOptions(
|
|
143
|
+
service=ec2.GatewayVpcEndpointAwsService.S3
|
|
144
|
+
)
|
|
145
|
+
}
|
|
146
|
+
if self.vpc_config.enable_s3_endpoint
|
|
147
|
+
else None
|
|
148
|
+
),
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
# Use either availability_zones or max_azs, not both
|
|
152
|
+
if availability_zones:
|
|
153
|
+
vpc_props["availability_zones"] = availability_zones
|
|
154
|
+
else:
|
|
155
|
+
vpc_props["max_azs"] = self.vpc_config.max_azs
|
|
156
|
+
|
|
157
|
+
# Create the VPC
|
|
158
|
+
vpc = ec2.Vpc(self, vpc_name, **vpc_props)
|
|
159
|
+
|
|
160
|
+
# Add interface endpoints if specified
|
|
161
|
+
if self.vpc_config.enable_interface_endpoints:
|
|
162
|
+
self._add_interface_endpoints(vpc, self.vpc_config.interface_endpoints)
|
|
163
|
+
|
|
164
|
+
# Add tags if specified
|
|
165
|
+
for key, value in self.vpc_config.tags.items():
|
|
166
|
+
cdk.Tags.of(vpc).add(key, value)
|
|
167
|
+
|
|
168
|
+
return vpc
|
|
169
|
+
|
|
170
|
+
def _get_subnet_configuration(self) -> List[ec2.SubnetConfiguration]:
|
|
171
|
+
"""Configure the subnets for the VPC"""
|
|
172
|
+
subnet_configs = []
|
|
173
|
+
|
|
174
|
+
# Public subnets
|
|
175
|
+
if self.vpc_config.subnets.get("public", {}).get("enabled", True):
|
|
176
|
+
public_config = self.vpc_config.subnets["public"]
|
|
177
|
+
subnet_configs.append(
|
|
178
|
+
ec2.SubnetConfiguration(
|
|
179
|
+
name=self.vpc_config.public_subnet_name,
|
|
180
|
+
subnet_type=ec2.SubnetType.PUBLIC,
|
|
181
|
+
cidr_mask=public_config.get("cidr_mask", 24),
|
|
182
|
+
map_public_ip_on_launch=public_config.get("map_public_ip", True),
|
|
183
|
+
)
|
|
184
|
+
)
|
|
185
|
+
|
|
186
|
+
# Private subnets
|
|
187
|
+
if self.vpc_config.subnets.get("private", {}).get("enabled", True):
|
|
188
|
+
private_config = self.vpc_config.subnets["private"]
|
|
189
|
+
subnet_configs.append(
|
|
190
|
+
ec2.SubnetConfiguration(
|
|
191
|
+
name=self.vpc_config.private_subnet_name,
|
|
192
|
+
subnet_type=ec2.SubnetType.PRIVATE_WITH_EGRESS,
|
|
193
|
+
cidr_mask=private_config.get("cidr_mask", 24),
|
|
194
|
+
)
|
|
195
|
+
)
|
|
196
|
+
|
|
197
|
+
# Isolated subnets
|
|
198
|
+
if self.vpc_config.subnets.get("isolated", {}).get("enabled", False):
|
|
199
|
+
isolated_config = self.vpc_config.subnets["isolated"]
|
|
200
|
+
subnet_configs.append(
|
|
201
|
+
ec2.SubnetConfiguration(
|
|
202
|
+
name=self.vpc_config.isolated_subnet_name,
|
|
203
|
+
subnet_type=ec2.SubnetType.PRIVATE_ISOLATED,
|
|
204
|
+
cidr_mask=isolated_config.get("cidr_mask", 24),
|
|
205
|
+
)
|
|
206
|
+
)
|
|
207
|
+
|
|
208
|
+
return subnet_configs
|
|
209
|
+
|
|
210
|
+
def _add_interface_endpoints(self, vpc: ec2.Vpc, endpoints: List[str]) -> None:
|
|
211
|
+
"""Add VPC interface endpoints"""
|
|
212
|
+
endpoint_services = {
|
|
213
|
+
"ecr.api": ec2.InterfaceVpcEndpointAwsService.ECR,
|
|
214
|
+
"ecr.dkr": ec2.InterfaceVpcEndpointAwsService.ECR_DOCKER,
|
|
215
|
+
"ec2": ec2.InterfaceVpcEndpointAwsService.EC2,
|
|
216
|
+
"ecs": ec2.InterfaceVpcEndpointAwsService.ECS,
|
|
217
|
+
"lambda": ec2.InterfaceVpcEndpointAwsService.LAMBDA,
|
|
218
|
+
"secretsmanager": ec2.InterfaceVpcEndpointAwsService.SECRETS_MANAGER,
|
|
219
|
+
"ssm": ec2.InterfaceVpcEndpointAwsService.SSM,
|
|
220
|
+
"kms": ec2.InterfaceVpcEndpointAwsService.KMS,
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
for endpoint_name in endpoints:
|
|
224
|
+
if endpoint_name in endpoint_services:
|
|
225
|
+
vpc.add_interface_endpoint(
|
|
226
|
+
f"{endpoint_name}-endpoint",
|
|
227
|
+
service=endpoint_services[endpoint_name],
|
|
228
|
+
subnets=ec2.SubnetSelection(
|
|
229
|
+
subnet_type=ec2.SubnetType.PRIVATE_WITH_EGRESS
|
|
230
|
+
),
|
|
231
|
+
)
|
|
232
|
+
logger.info(f"Added interface endpoint: {endpoint_name}")
|
|
233
|
+
else:
|
|
234
|
+
logger.warning(f"Unknown interface endpoint: {endpoint_name}")
|
|
235
|
+
|
|
236
|
+
def _add_outputs(self, vpc_name: str) -> None:
|
|
237
|
+
"""Add CloudFormation outputs for the VPC"""
|
|
238
|
+
if not self.vpc:
|
|
239
|
+
return
|
|
240
|
+
|
|
241
|
+
# VPC outputs
|
|
242
|
+
cdk.CfnOutput(
|
|
243
|
+
self,
|
|
244
|
+
f"{vpc_name}-VpcId",
|
|
245
|
+
value=self.vpc.vpc_id,
|
|
246
|
+
description=f"VPC ID for {vpc_name}",
|
|
247
|
+
export_name=f"{self.deployment.workload_name}-{self.deployment.environment}-vpc-id",
|
|
248
|
+
)
|
|
249
|
+
|
|
250
|
+
# Subnet outputs
|
|
251
|
+
public_subnet_ids = [subnet.subnet_id for subnet in self.vpc.public_subnets]
|
|
252
|
+
if public_subnet_ids:
|
|
253
|
+
cdk.CfnOutput(
|
|
254
|
+
self,
|
|
255
|
+
f"{vpc_name}-PublicSubnetIds",
|
|
256
|
+
value=",".join(public_subnet_ids),
|
|
257
|
+
description=f"Public subnet IDs for {vpc_name}",
|
|
258
|
+
export_name=f"{self.deployment.workload_name}-{self.deployment.environment}-public-subnet-ids",
|
|
259
|
+
)
|
|
260
|
+
|
|
261
|
+
private_subnet_ids = [subnet.subnet_id for subnet in self.vpc.private_subnets]
|
|
262
|
+
if private_subnet_ids:
|
|
263
|
+
cdk.CfnOutput(
|
|
264
|
+
self,
|
|
265
|
+
f"{vpc_name}-PrivateSubnetIds",
|
|
266
|
+
value=",".join(private_subnet_ids),
|
|
267
|
+
description=f"Private subnet IDs for {vpc_name}",
|
|
268
|
+
export_name=f"{self.deployment.workload_name}-{self.deployment.environment}-private-subnet-ids",
|
|
269
|
+
)
|
|
270
|
+
|
|
271
|
+
isolated_subnet_ids = [subnet.subnet_id for subnet in self.vpc.isolated_subnets]
|
|
272
|
+
if isolated_subnet_ids:
|
|
273
|
+
cdk.CfnOutput(
|
|
274
|
+
self,
|
|
275
|
+
f"{vpc_name}-IsolatedSubnetIds",
|
|
276
|
+
value=",".join(isolated_subnet_ids),
|
|
277
|
+
description=f"Isolated subnet IDs for {vpc_name}",
|
|
278
|
+
export_name=f"{self.deployment.workload_name}-{self.deployment.environment}-isolated-subnet-ids",
|
|
279
|
+
)
|
|
280
|
+
|
|
281
|
+
# Route table outputs - simplified to avoid route table access issues
|
|
282
|
+
# Skip route table outputs for now as they're causing CDK API issues
|
|
283
|
+
# public_route_table_ids = []
|
|
284
|
+
# if self.vpc.public_subnets:
|
|
285
|
+
# for subnet in self.vpc.public_subnets:
|
|
286
|
+
# # Access route table through the subnet's route table association
|
|
287
|
+
# for association in subnet.node.children:
|
|
288
|
+
# if hasattr(association, 'route_table_id') and association.route_table_id:
|
|
289
|
+
# public_route_table_ids.append(association.route_table_id)
|
|
290
|
+
#
|
|
291
|
+
# if public_route_table_ids:
|
|
292
|
+
# cdk.CfnOutput(
|
|
293
|
+
# self,
|
|
294
|
+
# f"{vpc_name}-PublicRouteTableIds",
|
|
295
|
+
# value=",".join(public_route_table_ids),
|
|
296
|
+
# description=f"Public route table IDs for {vpc_name}",
|
|
297
|
+
# export_name=f"{self.deployment.workload_name}-{self.deployment.environment}-public-route-table-ids",
|
|
298
|
+
# )
|
|
299
|
+
#
|
|
300
|
+
# private_route_table_ids = []
|
|
301
|
+
# if self.vpc.private_subnets:
|
|
302
|
+
# for subnet in self.vpc.private_subnets:
|
|
303
|
+
# # Access route table through the subnet's route table association
|
|
304
|
+
# for association in subnet.node.children:
|
|
305
|
+
# if hasattr(association, 'route_table_id') and association.route_table_id:
|
|
306
|
+
# private_route_table_ids.append(association.route_table_id)
|
|
307
|
+
#
|
|
308
|
+
# if private_route_table_ids:
|
|
309
|
+
# cdk.CfnOutput(
|
|
310
|
+
# self,
|
|
311
|
+
# f"{vpc_name}-PrivateRouteTableIds",
|
|
312
|
+
# value=",".join(private_route_table_ids),
|
|
313
|
+
# description=f"Private route table IDs for {vpc_name}",
|
|
314
|
+
# export_name=f"{self.deployment.workload_name}-{self.deployment.environment}-private-route-table-ids",
|
|
315
|
+
# )
|
|
316
|
+
|
|
317
|
+
# Internet Gateway output
|
|
318
|
+
if hasattr(self.vpc, 'internet_gateway_id') and self.vpc.internet_gateway_id:
|
|
319
|
+
cdk.CfnOutput(
|
|
320
|
+
self,
|
|
321
|
+
f"{vpc_name}-InternetGatewayId",
|
|
322
|
+
value=self.vpc.internet_gateway_id,
|
|
323
|
+
description=f"Internet Gateway ID for {vpc_name}",
|
|
324
|
+
export_name=f"{self.deployment.workload_name}-{self.deployment.environment}-internet-gateway-id",
|
|
325
|
+
)
|
|
326
|
+
|
|
327
|
+
# NAT Gateway outputs - simplified to avoid None values
|
|
328
|
+
nat_gateway_ids = []
|
|
329
|
+
for subnet in self.vpc.public_subnets:
|
|
330
|
+
if hasattr(subnet, 'node') and subnet.node:
|
|
331
|
+
for child in subnet.node.children:
|
|
332
|
+
if hasattr(child, 'nat_gateway_id') and child.nat_gateway_id:
|
|
333
|
+
nat_gateway_ids.append(child.nat_gateway_id)
|
|
334
|
+
|
|
335
|
+
if nat_gateway_ids:
|
|
336
|
+
cdk.CfnOutput(
|
|
337
|
+
self,
|
|
338
|
+
f"{vpc_name}-NatGatewayIds",
|
|
339
|
+
value=",".join(nat_gateway_ids),
|
|
340
|
+
description=f"NAT Gateway IDs for {vpc_name}",
|
|
341
|
+
export_name=f"{self.deployment.workload_name}-{self.deployment.environment}-nat-gateway-ids",
|
|
342
|
+
)
|
|
343
|
+
|
|
344
|
+
def _export_ssm_parameters(self) -> None:
|
|
345
|
+
"""Export SSM parameters using standardized approach"""
|
|
346
|
+
if not self.vpc:
|
|
347
|
+
logger.warning("No VPC to export")
|
|
348
|
+
return
|
|
349
|
+
|
|
350
|
+
# Prepare resource values for export
|
|
351
|
+
resource_values = {
|
|
352
|
+
"vpc_id": self.vpc.vpc_id,
|
|
353
|
+
"public_subnet_ids": [subnet.subnet_id for subnet in self.vpc.public_subnets],
|
|
354
|
+
"private_subnet_ids": [subnet.subnet_id for subnet in self.vpc.private_subnets],
|
|
355
|
+
"isolated_subnet_ids": [subnet.subnet_id for subnet in self.vpc.isolated_subnets],
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
# Add route table IDs if available - commented out due to CDK API issues
|
|
359
|
+
# public_route_table_ids = []
|
|
360
|
+
# if self.vpc.public_subnets:
|
|
361
|
+
# for subnet in self.vpc.public_subnets:
|
|
362
|
+
# # Access route table through the subnet's route table association
|
|
363
|
+
# for association in subnet.node.children:
|
|
364
|
+
# if hasattr(association, 'route_table_id') and association.route_table_id:
|
|
365
|
+
# public_route_table_ids.append(association.route_table_id)
|
|
366
|
+
#
|
|
367
|
+
# if public_route_table_ids:
|
|
368
|
+
# resource_values["public_route_table_ids"] = public_route_table_ids
|
|
369
|
+
#
|
|
370
|
+
# private_route_table_ids = []
|
|
371
|
+
# if self.vpc.private_subnets:
|
|
372
|
+
# for subnet in self.vpc.private_subnets:
|
|
373
|
+
# # Access route table through the subnet's route table association
|
|
374
|
+
# for association in subnet.node.children:
|
|
375
|
+
# if hasattr(association, 'route_table_id') and association.route_table_id:
|
|
376
|
+
# private_route_table_ids.append(association.route_table_id)
|
|
377
|
+
#
|
|
378
|
+
# if private_route_table_ids:
|
|
379
|
+
# resource_values["private_route_table_ids"] = private_route_table_ids
|
|
380
|
+
|
|
381
|
+
# Add NAT Gateway IDs if available - simplified to avoid None values
|
|
382
|
+
nat_gateway_ids = []
|
|
383
|
+
for subnet in self.vpc.public_subnets:
|
|
384
|
+
if hasattr(subnet, 'node') and subnet.node:
|
|
385
|
+
for child in subnet.node.children:
|
|
386
|
+
if hasattr(child, 'nat_gateway_id') and child.nat_gateway_id:
|
|
387
|
+
nat_gateway_ids.append(child.nat_gateway_id)
|
|
388
|
+
if nat_gateway_ids:
|
|
389
|
+
resource_values["nat_gateway_ids"] = nat_gateway_ids
|
|
390
|
+
|
|
391
|
+
# Add Internet Gateway ID if available
|
|
392
|
+
if hasattr(self.vpc, 'internet_gateway_id') and self.vpc.internet_gateway_id:
|
|
393
|
+
resource_values["internet_gateway_id"] = self.vpc.internet_gateway_id
|
|
394
|
+
|
|
395
|
+
# Export using standardized SSM mixin
|
|
396
|
+
exported_params = self.export_standardized_ssm_parameters(resource_values)
|
|
397
|
+
|
|
398
|
+
logger.info(f"Exported SSM parameters: {exported_params}")
|
|
399
|
+
|
|
400
|
+
# Backward compatibility methods
|
|
401
|
+
def auto_export_resources(self, resource_values: Dict[str, Any], context: Dict[str, Any] = None) -> Dict[str, str]:
|
|
402
|
+
"""Backward compatibility method for existing modules."""
|
|
403
|
+
return self.export_standardized_ssm_parameters(resource_values)
|
|
404
|
+
|
|
405
|
+
def auto_import_resources(self, context: Dict[str, Any] = None) -> Dict[str, Any]:
|
|
406
|
+
"""Backward compatibility method for existing modules."""
|
|
407
|
+
return self.get_all_ssm_imports()
|
|
408
|
+
|
|
409
|
+
|
|
410
|
+
# Backward compatibility alias
|
|
411
|
+
VpcStackStandardized = VpcStack
|
|
@@ -537,34 +537,42 @@ class ApiGatewayIntegrationUtility:
|
|
|
537
537
|
|
|
538
538
|
if ssm_path:
|
|
539
539
|
# Use enhanced SSM parameter import with auto-discovery support
|
|
540
|
-
from cdk_factory.interfaces.
|
|
541
|
-
|
|
540
|
+
from cdk_factory.interfaces.standardized_ssm_mixin import (
|
|
541
|
+
StandardizedSsmMixin,
|
|
542
542
|
)
|
|
543
543
|
|
|
544
|
-
ssm_mixin =
|
|
544
|
+
ssm_mixin = StandardizedSsmMixin()
|
|
545
545
|
|
|
546
546
|
# Setup enhanced SSM integration for auto-import
|
|
547
547
|
# Use "user-pool" as resource identifier for SSM paths to match cognito exports
|
|
548
|
-
|
|
548
|
+
api_gateway_config = stack_config.dictionary.get("api_gateway", {}).copy()
|
|
549
|
+
|
|
550
|
+
# Configure SSM imports for auto-discovery
|
|
551
|
+
if ssm_path == "auto":
|
|
552
|
+
if "ssm" not in api_gateway_config:
|
|
553
|
+
api_gateway_config["ssm"] = {}
|
|
554
|
+
if "imports" not in api_gateway_config["ssm"]:
|
|
555
|
+
api_gateway_config["ssm"]["imports"] = {}
|
|
556
|
+
api_gateway_config["ssm"]["imports"]["user_pool_arn"] = "/{{ORGANIZATION}}/{{ENVIRONMENT}}/cognito/user-pool/arn"
|
|
557
|
+
|
|
558
|
+
ssm_mixin.setup_standardized_ssm_integration(
|
|
549
559
|
scope=self.scope,
|
|
550
|
-
config=
|
|
560
|
+
config=api_gateway_config,
|
|
551
561
|
resource_type="cognito",
|
|
552
562
|
resource_name="user-pool",
|
|
553
563
|
)
|
|
554
564
|
|
|
555
|
-
#
|
|
565
|
+
# Get user pool ARN using new pattern - read directly from config
|
|
556
566
|
if ssm_path == "auto":
|
|
557
567
|
logger.info("Using auto-import for user pool ARN")
|
|
558
|
-
|
|
559
|
-
user_pool_arn =
|
|
568
|
+
ssm_imports = api_gateway_config.get("ssm", {}).get("imports", {})
|
|
569
|
+
user_pool_arn = ssm_imports.get("user_pool_arn")
|
|
560
570
|
else:
|
|
561
571
|
# Use direct parameter import for specific SSM path
|
|
562
572
|
logger.info(
|
|
563
573
|
f"Looking up user pool ARN from SSM parameter: {ssm_path}"
|
|
564
574
|
)
|
|
565
|
-
user_pool_arn = ssm_mixin.
|
|
566
|
-
ssm_path, "user_pool_arn"
|
|
567
|
-
)
|
|
575
|
+
user_pool_arn = ssm_mixin._resolve_single_ssm_import(ssm_path, "user_pool_arn")
|
|
568
576
|
|
|
569
577
|
# Extract user pool ID from ARN if we have it
|
|
570
578
|
if user_pool_arn and not user_pool_id:
|
|
@@ -866,15 +874,15 @@ class ApiGatewayIntegrationUtility:
|
|
|
866
874
|
|
|
867
875
|
if ssm_config.get("enabled", False):
|
|
868
876
|
try:
|
|
869
|
-
from cdk_factory.interfaces.
|
|
870
|
-
|
|
877
|
+
from cdk_factory.interfaces.standardized_ssm_mixin import (
|
|
878
|
+
StandardizedSsmMixin,
|
|
871
879
|
)
|
|
872
880
|
|
|
873
|
-
ssm_mixin =
|
|
881
|
+
ssm_mixin = StandardizedSsmMixin()
|
|
874
882
|
|
|
875
883
|
# Setup enhanced SSM integration for auto-import
|
|
876
884
|
# Use consistent resource name for cross-stack compatibility
|
|
877
|
-
ssm_mixin.
|
|
885
|
+
ssm_mixin.setup_standardized_ssm_integration(
|
|
878
886
|
scope=self.scope,
|
|
879
887
|
config=api_gateway_config,
|
|
880
888
|
resource_type="api-gateway",
|
|
@@ -900,7 +908,7 @@ class ApiGatewayIntegrationUtility:
|
|
|
900
908
|
logger.info(
|
|
901
909
|
f"Looking up authorizer ID from SSM parameter: {import_value}"
|
|
902
910
|
)
|
|
903
|
-
authorizer_id = ssm_mixin.
|
|
911
|
+
authorizer_id = ssm_mixin._resolve_single_ssm_import(
|
|
904
912
|
import_value, "authorizer_id"
|
|
905
913
|
)
|
|
906
914
|
if authorizer_id:
|
|
@@ -23,9 +23,9 @@ logger = Logger(__name__)
|
|
|
23
23
|
|
|
24
24
|
class EnvironmentVariables:
|
|
25
25
|
"""
|
|
26
|
-
Easy access to
|
|
27
|
-
It's a best practice to use this vs doing and os.
|
|
28
|
-
This helps us track all the
|
|
26
|
+
Easy access to allow the environment variables we use in the application.
|
|
27
|
+
It's a best practice to use this vs doing and os.getenv in each application.
|
|
28
|
+
This helps us track all the environment variables in use
|
|
29
29
|
"""
|
|
30
30
|
|
|
31
31
|
@staticmethod
|
|
@@ -212,7 +212,7 @@ class JsonLoadingUtility:
|
|
|
212
212
|
replacements = {
|
|
213
213
|
"{{workload-name}}": "geekcafe",
|
|
214
214
|
"{{deployment-name}}": "dev",
|
|
215
|
-
"{{awsAccount}}": "123456789012",
|
|
215
|
+
"{{awsAccount}}": os.environ.get("AWS_ACCOUNT", "123456789012"),
|
|
216
216
|
"{{hostedZoneName}}": "sandbox.geekcafe.com",
|
|
217
217
|
"{{placeholder}}": "DYNAMIC_VALUE"
|
|
218
218
|
}
|