cdk-factory 0.16.15__py3-none-any.whl → 0.20.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.
Potentially problematic release.
This version of cdk-factory might be problematic. Click here for more details.
- cdk_factory/configurations/base_config.py +23 -24
- cdk_factory/configurations/cdk_config.py +1 -1
- cdk_factory/configurations/deployment.py +12 -0
- cdk_factory/configurations/devops.py +1 -1
- cdk_factory/configurations/resources/acm.py +9 -2
- cdk_factory/configurations/resources/auto_scaling.py +7 -5
- cdk_factory/configurations/resources/cloudfront.py +7 -2
- cdk_factory/configurations/resources/ecr.py +1 -1
- cdk_factory/configurations/resources/ecs_cluster.py +12 -5
- cdk_factory/configurations/resources/ecs_service.py +30 -3
- cdk_factory/configurations/resources/lambda_edge.py +18 -4
- cdk_factory/configurations/resources/load_balancer.py +8 -9
- cdk_factory/configurations/resources/monitoring.py +8 -3
- cdk_factory/configurations/resources/rds.py +8 -9
- cdk_factory/configurations/resources/route53.py +5 -0
- cdk_factory/configurations/resources/rum.py +7 -2
- cdk_factory/configurations/resources/s3.py +10 -2
- 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/cloudfront/cloudfront_distribution_construct.py +1 -1
- 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 +684 -0
- cdk_factory/interfaces/vpc_provider_mixin.py +64 -33
- 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 +7 -17
- cdk_factory/stack_library/api_gateway/api_gateway_stack.py +84 -59
- cdk_factory/stack_library/auto_scaling/auto_scaling_stack.py +454 -537
- cdk_factory/stack_library/cloudfront/cloudfront_stack.py +76 -22
- cdk_factory/stack_library/code_artifact/code_artifact_stack.py +5 -27
- 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 -3
- cdk_factory/stack_library/ecs/ecs_cluster_stack.py +159 -75
- cdk_factory/stack_library/ecs/ecs_service_stack.py +59 -52
- cdk_factory/stack_library/lambda_edge/EDGE_LOG_RETENTION_TODO.md +226 -0
- cdk_factory/stack_library/lambda_edge/LAMBDA_EDGE_LOG_RETENTION_BLOG.md +215 -0
- cdk_factory/stack_library/lambda_edge/lambda_edge_stack.py +240 -83
- cdk_factory/stack_library/load_balancer/load_balancer_stack.py +139 -212
- cdk_factory/stack_library/rds/rds_stack.py +74 -98
- cdk_factory/stack_library/route53/route53_stack.py +246 -40
- cdk_factory/stack_library/rum/rum_stack.py +108 -91
- cdk_factory/stack_library/security_group/security_group_full_stack.py +10 -53
- cdk_factory/stack_library/security_group/security_group_stack.py +12 -19
- cdk_factory/stack_library/simple_queue_service/sqs_stack.py +1 -34
- cdk_factory/stack_library/stack_base.py +5 -0
- cdk_factory/stack_library/vpc/vpc_stack.py +171 -130
- cdk_factory/stack_library/websites/static_website_stack.py +7 -3
- cdk_factory/utilities/api_gateway_integration_utility.py +24 -16
- cdk_factory/utilities/environment_services.py +5 -5
- 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.20.0.dist-info}/METADATA +1 -1
- {cdk_factory-0.16.15.dist-info → cdk_factory-0.20.0.dist-info}/RECORD +64 -62
- cdk_factory/interfaces/enhanced_ssm_parameter_mixin.py +0 -321
- cdk_factory/interfaces/ssm_parameter_mixin.py +0 -454
- {cdk_factory-0.16.15.dist-info → cdk_factory-0.20.0.dist-info}/WHEEL +0 -0
- {cdk_factory-0.16.15.dist-info → cdk_factory-0.20.0.dist-info}/entry_points.txt +0 -0
- {cdk_factory-0.16.15.dist-info → cdk_factory-0.20.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"""
|
|
2
|
-
VPC Stack Pattern for CDK-Factory
|
|
2
|
+
VPC Stack Pattern for CDK-Factory (Standardized SSM Version)
|
|
3
3
|
Maintainers: Eric Wilson
|
|
4
4
|
MIT License. See Project Root for the license information.
|
|
5
5
|
"""
|
|
@@ -15,23 +15,35 @@ from cdk_factory.configurations.deployment import DeploymentConfig
|
|
|
15
15
|
from cdk_factory.configurations.stack import StackConfig
|
|
16
16
|
from cdk_factory.configurations.resources.vpc import VpcConfig
|
|
17
17
|
from cdk_factory.interfaces.istack import IStack
|
|
18
|
-
from cdk_factory.interfaces.
|
|
18
|
+
from cdk_factory.interfaces.standardized_ssm_mixin import StandardizedSsmMixin
|
|
19
19
|
from cdk_factory.stack.stack_module_registry import register_stack
|
|
20
20
|
from cdk_factory.workload.workload_factory import WorkloadConfig
|
|
21
21
|
|
|
22
|
-
logger = Logger(service="
|
|
22
|
+
logger = Logger(service="VpcStackStandardized")
|
|
23
23
|
|
|
24
24
|
|
|
25
25
|
@register_stack("vpc_library_module")
|
|
26
26
|
@register_stack("vpc_stack")
|
|
27
|
-
class VpcStack(IStack,
|
|
27
|
+
class VpcStack(IStack, StandardizedSsmMixin):
|
|
28
28
|
"""
|
|
29
|
-
Reusable stack for AWS VPC.
|
|
30
|
-
|
|
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
|
|
31
40
|
"""
|
|
32
41
|
|
|
33
42
|
def __init__(self, scope: Construct, id: str, **kwargs) -> None:
|
|
43
|
+
# Initialize parent classes properly
|
|
34
44
|
super().__init__(scope, id, **kwargs)
|
|
45
|
+
|
|
46
|
+
# Initialize module attributes
|
|
35
47
|
self.vpc_config = None
|
|
36
48
|
self.stack_config = None
|
|
37
49
|
self.deployment = None
|
|
@@ -61,15 +73,21 @@ class VpcStack(IStack, EnhancedSsmParameterMixin):
|
|
|
61
73
|
self.vpc_config = VpcConfig(stack_config.dictionary.get("vpc", {}), deployment)
|
|
62
74
|
vpc_name = deployment.build_resource_name(self.vpc_config.name)
|
|
63
75
|
|
|
64
|
-
# Setup
|
|
65
|
-
self.
|
|
76
|
+
# Setup standardized SSM integration
|
|
77
|
+
self.setup_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_ssm_imports()
|
|
66
88
|
|
|
67
89
|
# Import any required resources from SSM
|
|
68
|
-
imported_resources = self.
|
|
69
|
-
"deployment_name": deployment.name,
|
|
70
|
-
"environment": deployment.environment,
|
|
71
|
-
"workload_name": workload.name
|
|
72
|
-
})
|
|
90
|
+
imported_resources = self.get_all_ssm_imports()
|
|
73
91
|
|
|
74
92
|
if imported_resources:
|
|
75
93
|
logger.info(f"Imported resources from SSM: {list(imported_resources.keys())}")
|
|
@@ -79,6 +97,11 @@ class VpcStack(IStack, EnhancedSsmParameterMixin):
|
|
|
79
97
|
|
|
80
98
|
# Add outputs
|
|
81
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")
|
|
82
105
|
|
|
83
106
|
def _create_vpc(self, vpc_name: str) -> ec2.Vpc:
|
|
84
107
|
"""Create a VPC with the specified configuration"""
|
|
@@ -97,23 +120,25 @@ class VpcStack(IStack, EnhancedSsmParameterMixin):
|
|
|
97
120
|
# Explicitly list AZs for the region to avoid dummy values
|
|
98
121
|
max_azs = self.vpc_config.max_azs or 2
|
|
99
122
|
if region == "us-east-1":
|
|
100
|
-
availability_zones = [
|
|
123
|
+
availability_zones = ["us-east-1a", "us-east-1b", "us-east-1c"][:max_azs]
|
|
101
124
|
elif region == "us-east-2":
|
|
102
|
-
availability_zones = [
|
|
125
|
+
availability_zones = ["us-east-2a", "us-east-2b", "us-east-2c"][:max_azs]
|
|
103
126
|
elif region == "us-west-1":
|
|
104
|
-
availability_zones = [
|
|
127
|
+
availability_zones = ["us-west-1a", "us-west-1c"][:max_azs]
|
|
105
128
|
elif region == "us-west-2":
|
|
106
|
-
availability_zones = [
|
|
107
|
-
|
|
129
|
+
availability_zones = ["us-west-2a", "us-west-2b", "us-west-2c"][:max_azs]
|
|
130
|
+
|
|
108
131
|
# Build VPC properties
|
|
109
|
-
# Note: CDK doesn't allow both 'availability_zones' and 'max_azs' - use one or the other
|
|
110
132
|
vpc_props = {
|
|
111
133
|
"vpc_name": vpc_name,
|
|
112
|
-
"
|
|
134
|
+
"ip_addresses": ec2.IpAddresses.cidr(self.vpc_config.cidr),
|
|
113
135
|
"nat_gateways": nat_gateway_count,
|
|
114
136
|
"subnet_configuration": subnet_configuration,
|
|
115
137
|
"enable_dns_hostnames": self.vpc_config.enable_dns_hostnames,
|
|
116
138
|
"enable_dns_support": self.vpc_config.enable_dns_support,
|
|
139
|
+
"max_azs": self.vpc_config.max_azs if not availability_zones else None,
|
|
140
|
+
"availability_zones": availability_zones, # Use explicit AZs when available
|
|
141
|
+
"restrict_default_security_group": self.vpc_config.get("restrict_default_security_group", False),
|
|
117
142
|
"gateway_endpoints": (
|
|
118
143
|
{
|
|
119
144
|
"S3": ec2.GatewayVpcEndpointOptions(
|
|
@@ -125,15 +150,19 @@ class VpcStack(IStack, EnhancedSsmParameterMixin):
|
|
|
125
150
|
),
|
|
126
151
|
}
|
|
127
152
|
|
|
128
|
-
# Use either availability_zones or max_azs, not both
|
|
129
|
-
if availability_zones:
|
|
130
|
-
vpc_props["availability_zones"] = availability_zones
|
|
131
|
-
else:
|
|
132
|
-
vpc_props["max_azs"] = self.vpc_config.max_azs
|
|
133
|
-
|
|
134
153
|
# Create the VPC
|
|
135
154
|
vpc = ec2.Vpc(self, vpc_name, **vpc_props)
|
|
136
155
|
|
|
156
|
+
# Add IAM permissions for default security group restriction if enabled
|
|
157
|
+
if self.vpc_config.get("restrict_default_security_group", False):
|
|
158
|
+
self._add_default_sg_restriction_permissions(vpc)
|
|
159
|
+
else:
|
|
160
|
+
# Note: When disabling, existing restrictions remain
|
|
161
|
+
# This is AWS CDK's behavior - custom resources clean up themselves,
|
|
162
|
+
# but security group rules they created persist
|
|
163
|
+
# Users can manually clean up if needed via AWS Console
|
|
164
|
+
pass
|
|
165
|
+
|
|
137
166
|
# Add interface endpoints if specified
|
|
138
167
|
if self.vpc_config.enable_interface_endpoints:
|
|
139
168
|
self._add_interface_endpoints(vpc, self.vpc_config.interface_endpoints)
|
|
@@ -147,152 +176,164 @@ class VpcStack(IStack, EnhancedSsmParameterMixin):
|
|
|
147
176
|
def _get_subnet_configuration(self) -> List[ec2.SubnetConfiguration]:
|
|
148
177
|
"""Configure the subnets for the VPC"""
|
|
149
178
|
subnet_configs = []
|
|
150
|
-
|
|
179
|
+
|
|
151
180
|
# Public subnets
|
|
152
|
-
if self.vpc_config.
|
|
181
|
+
if self.vpc_config.subnets.get("public", {}).get("enabled", True):
|
|
182
|
+
public_config = self.vpc_config.subnets["public"]
|
|
153
183
|
subnet_configs.append(
|
|
154
184
|
ec2.SubnetConfiguration(
|
|
155
185
|
name=self.vpc_config.public_subnet_name,
|
|
156
186
|
subnet_type=ec2.SubnetType.PUBLIC,
|
|
157
|
-
cidr_mask=
|
|
187
|
+
cidr_mask=public_config.get("cidr_mask", 24),
|
|
188
|
+
map_public_ip_on_launch=public_config.get("map_public_ip", True),
|
|
158
189
|
)
|
|
159
190
|
)
|
|
160
|
-
|
|
191
|
+
|
|
161
192
|
# Private subnets
|
|
162
|
-
if self.vpc_config.
|
|
193
|
+
if self.vpc_config.subnets.get("private", {}).get("enabled", True):
|
|
194
|
+
private_config = self.vpc_config.subnets["private"]
|
|
163
195
|
subnet_configs.append(
|
|
164
196
|
ec2.SubnetConfiguration(
|
|
165
197
|
name=self.vpc_config.private_subnet_name,
|
|
166
198
|
subnet_type=ec2.SubnetType.PRIVATE_WITH_EGRESS,
|
|
167
|
-
cidr_mask=
|
|
199
|
+
cidr_mask=private_config.get("cidr_mask", 24),
|
|
168
200
|
)
|
|
169
201
|
)
|
|
170
|
-
|
|
202
|
+
|
|
171
203
|
# Isolated subnets
|
|
172
|
-
if self.vpc_config.
|
|
204
|
+
if self.vpc_config.subnets.get("isolated", {}).get("enabled", False):
|
|
205
|
+
isolated_config = self.vpc_config.subnets["isolated"]
|
|
173
206
|
subnet_configs.append(
|
|
174
207
|
ec2.SubnetConfiguration(
|
|
175
208
|
name=self.vpc_config.isolated_subnet_name,
|
|
176
209
|
subnet_type=ec2.SubnetType.PRIVATE_ISOLATED,
|
|
177
|
-
cidr_mask=
|
|
210
|
+
cidr_mask=isolated_config.get("cidr_mask", 24),
|
|
178
211
|
)
|
|
179
212
|
)
|
|
180
|
-
|
|
213
|
+
|
|
181
214
|
return subnet_configs
|
|
182
215
|
|
|
183
|
-
def _get_nat_gateway_configuration(self) -> Dict[str, Any]:
|
|
184
|
-
"""Configure NAT gateways for the VPC"""
|
|
185
|
-
return self.vpc_config.nat_gateways
|
|
186
|
-
|
|
187
216
|
def _add_interface_endpoints(self, vpc: ec2.Vpc, endpoints: List[str]) -> None:
|
|
188
|
-
"""Add interface endpoints
|
|
189
|
-
# Common interface endpoints
|
|
217
|
+
"""Add VPC interface endpoints"""
|
|
190
218
|
endpoint_services = {
|
|
191
219
|
"ecr.api": ec2.InterfaceVpcEndpointAwsService.ECR,
|
|
192
220
|
"ecr.dkr": ec2.InterfaceVpcEndpointAwsService.ECR_DOCKER,
|
|
193
|
-
"
|
|
194
|
-
"
|
|
221
|
+
"ec2": ec2.InterfaceVpcEndpointAwsService.EC2,
|
|
222
|
+
"ecs": ec2.InterfaceVpcEndpointAwsService.ECS,
|
|
223
|
+
"lambda": ec2.InterfaceVpcEndpointAwsService.LAMBDA,
|
|
195
224
|
"secretsmanager": ec2.InterfaceVpcEndpointAwsService.SECRETS_MANAGER,
|
|
196
|
-
"
|
|
197
|
-
"
|
|
225
|
+
"ssm": ec2.InterfaceVpcEndpointAwsService.SSM,
|
|
226
|
+
"kms": ec2.InterfaceVpcEndpointAwsService.KMS,
|
|
198
227
|
}
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
if endpoint in endpoint_services:
|
|
228
|
+
|
|
229
|
+
for endpoint_name in endpoints:
|
|
230
|
+
if endpoint_name in endpoint_services:
|
|
203
231
|
vpc.add_interface_endpoint(
|
|
204
|
-
f"{
|
|
232
|
+
f"{endpoint_name}-endpoint",
|
|
233
|
+
service=endpoint_services[endpoint_name],
|
|
234
|
+
subnets=ec2.SubnetSelection(
|
|
235
|
+
subnet_type=ec2.SubnetType.PRIVATE_WITH_EGRESS
|
|
236
|
+
),
|
|
205
237
|
)
|
|
238
|
+
logger.info(f"Added interface endpoint: {endpoint_name}")
|
|
206
239
|
else:
|
|
207
|
-
logger.warning(f"
|
|
240
|
+
logger.warning(f"Unknown interface endpoint: {endpoint_name}")
|
|
208
241
|
|
|
209
242
|
def _add_outputs(self, vpc_name: str) -> None:
|
|
210
243
|
"""Add CloudFormation outputs for the VPC"""
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
self,
|
|
214
|
-
f"{vpc_name}-id",
|
|
215
|
-
value=self.vpc.vpc_id,
|
|
216
|
-
export_name=f"{self.deployment.build_resource_name(vpc_name)}-id",
|
|
217
|
-
)
|
|
218
|
-
|
|
219
|
-
cdk.CfnOutput(
|
|
220
|
-
self,
|
|
221
|
-
f"{vpc_name}-public-subnets",
|
|
222
|
-
value=",".join(
|
|
223
|
-
[subnet.subnet_id for subnet in self.vpc.public_subnets]
|
|
224
|
-
),
|
|
225
|
-
export_name=f"{self.deployment.build_resource_name(vpc_name)}-public-subnets",
|
|
226
|
-
)
|
|
227
|
-
|
|
228
|
-
if self.vpc.private_subnets:
|
|
229
|
-
cdk.CfnOutput(
|
|
230
|
-
self,
|
|
231
|
-
f"{vpc_name}-private-subnets",
|
|
232
|
-
value=",".join(
|
|
233
|
-
[subnet.subnet_id for subnet in self.vpc.private_subnets]
|
|
234
|
-
),
|
|
235
|
-
export_name=f"{self.deployment.build_resource_name(vpc_name)}-private-subnets",
|
|
236
|
-
)
|
|
244
|
+
return
|
|
245
|
+
|
|
237
246
|
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
self,
|
|
241
|
-
f"{vpc_name}-isolated-subnets",
|
|
242
|
-
value=",".join(
|
|
243
|
-
[subnet.subnet_id for subnet in self.vpc.isolated_subnets]
|
|
244
|
-
),
|
|
245
|
-
export_name=f"{self.deployment.build_resource_name(vpc_name)}-isolated-subnets",
|
|
246
|
-
)
|
|
247
|
-
|
|
248
|
-
# Export SSM parameters if configured
|
|
249
|
-
self._export_ssm_parameters(vpc_name)
|
|
250
|
-
|
|
251
|
-
def _export_ssm_parameters(self, vpc_name: str) -> None:
|
|
252
|
-
"""Export VPC resources to SSM Parameter Store using enhanced auto-export"""
|
|
247
|
+
def _export_ssm_parameters(self) -> None:
|
|
248
|
+
"""Export SSM parameters using standardized approach"""
|
|
253
249
|
if not self.vpc:
|
|
250
|
+
logger.warning("No VPC to export")
|
|
254
251
|
return
|
|
255
252
|
|
|
256
|
-
#
|
|
257
|
-
|
|
253
|
+
# Prepare resource values for export
|
|
254
|
+
resource_values = {
|
|
258
255
|
"vpc_id": self.vpc.vpc_id,
|
|
259
|
-
"
|
|
256
|
+
"public_subnet_ids": ",".join([subnet.subnet_id for subnet in self.vpc.public_subnets]) if self.vpc.public_subnets else "",
|
|
257
|
+
"private_subnet_ids": ",".join([subnet.subnet_id for subnet in self.vpc.private_subnets]) if self.vpc.private_subnets else "",
|
|
258
|
+
"isolated_subnet_ids": ",".join([subnet.subnet_id for subnet in self.vpc.isolated_subnets]) if self.vpc.isolated_subnets else "",
|
|
260
259
|
}
|
|
260
|
+
|
|
261
|
+
# Add route table IDs if available - commented out due to CDK API issues
|
|
262
|
+
# public_route_table_ids = []
|
|
263
|
+
# if self.vpc.public_subnets:
|
|
264
|
+
# for subnet in self.vpc.public_subnets:
|
|
265
|
+
# # Access route table through the subnet's route table association
|
|
266
|
+
# for association in subnet.node.children:
|
|
267
|
+
# if hasattr(association, 'route_table_id') and association.route_table_id:
|
|
268
|
+
# public_route_table_ids.append(association.route_table_id)
|
|
269
|
+
#
|
|
270
|
+
# if public_route_table_ids:
|
|
271
|
+
# resource_values["public_route_table_ids"] = public_route_table_ids
|
|
272
|
+
#
|
|
273
|
+
# private_route_table_ids = []
|
|
274
|
+
# if self.vpc.private_subnets:
|
|
275
|
+
# for subnet in self.vpc.private_subnets:
|
|
276
|
+
# # Access route table through the subnet's route table association
|
|
277
|
+
# for association in subnet.node.children:
|
|
278
|
+
# if hasattr(association, 'route_table_id') and association.route_table_id:
|
|
279
|
+
# private_route_table_ids.append(association.route_table_id)
|
|
280
|
+
#
|
|
281
|
+
# if private_route_table_ids:
|
|
282
|
+
# resource_values["private_route_table_ids"] = private_route_table_ids
|
|
283
|
+
|
|
284
|
+
# Add NAT Gateway IDs if available - simplified to avoid None values
|
|
285
|
+
nat_gateway_ids = []
|
|
286
|
+
for subnet in self.vpc.public_subnets:
|
|
287
|
+
if hasattr(subnet, 'node') and subnet.node:
|
|
288
|
+
for child in subnet.node.children:
|
|
289
|
+
if hasattr(child, 'nat_gateway_id') and child.nat_gateway_id:
|
|
290
|
+
nat_gateway_ids.append(child.nat_gateway_id)
|
|
291
|
+
if nat_gateway_ids:
|
|
292
|
+
resource_values["nat_gateway_ids"] = ",".join(nat_gateway_ids)
|
|
293
|
+
|
|
294
|
+
# Add Internet Gateway ID if available
|
|
295
|
+
if hasattr(self.vpc, 'internet_gateway_id') and self.vpc.internet_gateway_id:
|
|
296
|
+
resource_values["internet_gateway_id"] = self.vpc.internet_gateway_id
|
|
261
297
|
|
|
262
|
-
#
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
)
|
|
267
|
-
|
|
268
|
-
if self.vpc.private_subnets:
|
|
269
|
-
vpc_resources["private_subnet_ids"] = ",".join(
|
|
270
|
-
[subnet.subnet_id for subnet in self.vpc.private_subnets]
|
|
271
|
-
)
|
|
272
|
-
|
|
273
|
-
if hasattr(self.vpc, "isolated_subnets") and self.vpc.isolated_subnets:
|
|
274
|
-
vpc_resources["isolated_subnet_ids"] = ",".join(
|
|
275
|
-
[subnet.subnet_id for subnet in self.vpc.isolated_subnets]
|
|
276
|
-
)
|
|
298
|
+
# Export using standardized SSM mixin
|
|
299
|
+
exported_params = self.export_ssm_parameters(resource_values)
|
|
300
|
+
|
|
301
|
+
logger.info(f"Exported SSM parameters: {exported_params}")
|
|
277
302
|
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
"environment": self.deployment.environment,
|
|
282
|
-
"workload_name": self.workload.name
|
|
283
|
-
}
|
|
303
|
+
def _add_default_sg_restriction_permissions(self, vpc: ec2.Vpc) -> None:
|
|
304
|
+
"""
|
|
305
|
+
Add IAM permissions required for default security group restriction.
|
|
284
306
|
|
|
285
|
-
|
|
307
|
+
CDK creates a custom resource that needs ec2:AuthorizeSecurityGroupIngress
|
|
308
|
+
permission to restrict the default security group.
|
|
309
|
+
"""
|
|
310
|
+
from aws_cdk import aws_iam as iam
|
|
286
311
|
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
312
|
+
# Find the custom resource role that CDK creates for default SG restriction
|
|
313
|
+
# The role follows a naming pattern: {VpcName}-CustomVpcRestrictDefaultSGCustomResource*
|
|
314
|
+
|
|
315
|
+
# Grant the required permissions to all roles in this stack that might need it
|
|
316
|
+
# This is a broad approach since we can't easily predict the exact role name
|
|
317
|
+
for child in self.node.children:
|
|
318
|
+
if hasattr(child, 'role') and hasattr(child.role, 'add_to_policy'):
|
|
319
|
+
child.role.add_to_policy(iam.PolicyStatement(
|
|
320
|
+
actions=[
|
|
321
|
+
"ec2:AuthorizeSecurityGroupIngress",
|
|
322
|
+
"ec2:RevokeSecurityGroupIngress",
|
|
323
|
+
"ec2:UpdateSecurityGroupRuleDescriptionsIngress"
|
|
324
|
+
],
|
|
325
|
+
resources=[vpc.vpc_default_security_group.security_group_arn]
|
|
326
|
+
))
|
|
327
|
+
|
|
328
|
+
# Backward compatibility methods
|
|
329
|
+
def auto_export_resources(self, resource_values: Dict[str, Any], context: Dict[str, Any] = None) -> Dict[str, str]:
|
|
330
|
+
"""Backward compatibility method for existing modules."""
|
|
331
|
+
return self.export_ssm_parameters(resource_values)
|
|
332
|
+
|
|
333
|
+
def auto_import_resources(self, context: Dict[str, Any] = None) -> Dict[str, Any]:
|
|
334
|
+
"""Backward compatibility method for existing modules."""
|
|
335
|
+
return self.get_all_ssm_imports()
|
|
336
|
+
|
|
337
|
+
|
|
338
|
+
# Backward compatibility alias
|
|
339
|
+
VpcStackStandardized = VpcStack
|
|
@@ -113,12 +113,16 @@ class StaticWebSiteStack(IStack):
|
|
|
113
113
|
self, stack_config: StackConfig, workload: WorkloadConfig
|
|
114
114
|
) -> str:
|
|
115
115
|
source = stack_config.dictionary.get("src", {}).get("path")
|
|
116
|
+
if not source:
|
|
117
|
+
raise ValueError("Source path is required for static website stack")
|
|
116
118
|
for base in workload.paths:
|
|
117
|
-
|
|
119
|
+
if base is None:
|
|
120
|
+
continue
|
|
121
|
+
candidate = Path(os.path.join(str(Path(base)), source)).resolve()
|
|
118
122
|
|
|
119
123
|
if candidate.exists():
|
|
120
124
|
return str(candidate)
|
|
121
|
-
raise ValueError(f"Could not find the source path: {source}")
|
|
125
|
+
raise ValueError(f"Could not find the source path for static site: {source}")
|
|
122
126
|
|
|
123
127
|
def __setup_cloudfront_distribution(
|
|
124
128
|
self,
|
|
@@ -225,7 +229,7 @@ class StaticWebSiteStack(IStack):
|
|
|
225
229
|
bucket: The S3 bucket
|
|
226
230
|
cloudfront_distribution: The CloudFront distribution construct
|
|
227
231
|
"""
|
|
228
|
-
ssm_exports = stack_config.dictionary.get("
|
|
232
|
+
ssm_exports = stack_config.dictionary.get("ssm", {}).get("exports", {})
|
|
229
233
|
|
|
230
234
|
if not ssm_exports:
|
|
231
235
|
logger.debug("No SSM exports configured for this stack")
|
|
@@ -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_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_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
|
|
@@ -173,9 +173,9 @@ class EnvironmentServices:
|
|
|
173
173
|
environment = {}
|
|
174
174
|
# more verbose
|
|
175
175
|
environment["WORKLOAD_NAME"] = deployment.workload.get("name", "NA")
|
|
176
|
-
environment["ENVIRONMENT_NAME"] = deployment.environment
|
|
176
|
+
environment["ENVIRONMENT_NAME"] = deployment.workload.get("environment", deployment.environment)
|
|
177
177
|
environment["DEPLOYMENT_NAME"] = deployment.name
|
|
178
|
-
environment["ENVIRONMENT"] = deployment.environment
|
|
178
|
+
environment["ENVIRONMENT"] = deployment.workload.get("environment", deployment.environment)
|
|
179
179
|
environment["PIPELINE"] = deployment.pipeline.get("name", "NA")
|
|
180
180
|
environment["ACCOUNT"] = deployment.account
|
|
181
181
|
environment["DEPLOYMENT"] = deployment.name
|
|
@@ -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
|
}
|