cdk-factory 0.15.7__py3-none-any.whl → 0.15.9__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.

@@ -69,3 +69,16 @@ class SecurityGroupFullStackConfig:
69
69
  if "ssm" in self.__config and "imports" in self.__config["ssm"]:
70
70
  return self.__config["ssm"]["imports"]
71
71
  return self.__config.get("ssm_imports", {})
72
+
73
+ @property
74
+ def ssm_exports(self) -> Dict[str, str]:
75
+ """SSM parameter exports for the Security Group"""
76
+ # Check both nested and flat structures for backwards compatibility
77
+ if "ssm" in self.__config and "exports" in self.__config["ssm"]:
78
+ return self.__config["ssm"]["exports"]
79
+ return self.__config.get("ssm_exports", {})
80
+
81
+ @property
82
+ def security_groups(self) -> List[Dict[str, Any]]:
83
+ """List of security groups to create"""
84
+ return self.__config.get("security_groups", [])
@@ -113,7 +113,7 @@ def lambda_handler(event, context):
113
113
  Configuration (fetched from SSM Parameter Store):
114
114
  - GATE_ENABLED: Whether IP gating is enabled (true/false)
115
115
  - ALLOW_CIDRS: Comma-separated list of allowed CIDR ranges
116
- - MAINT_CF_HOST: CloudFront domain for maintenance/lockout page
116
+ - DNS_ALIAS: CloudFront domain for backup/lockout page
117
117
 
118
118
  Runtime configuration is bundled in runtime_config.json by CDK.
119
119
  SSM parameter paths are auto-generated by CDK as:
@@ -172,9 +172,9 @@ def lambda_handler(event, context):
172
172
  print(f"IP gating is disabled (GATE_ENABLED={gate_enabled or 'NONE'})")
173
173
  return request
174
174
 
175
- # Get allowed CIDRs and maintenance host
175
+ # Get allowed CIDRs and backup host
176
176
  allow_cidrs_str = get_ssm_parameter(f'/{env}/{function_name}/allow-cidrs', 'us-east-1')
177
- maint_cf_host = get_ssm_parameter(f'/{env}/{function_name}/maint-cf-host', 'us-east-1')
177
+ dns_alias = get_ssm_parameter(f'/{env}/{function_name}/dns-alias', 'us-east-1')
178
178
 
179
179
  # Parse allowed CIDRs (empty string results in empty list)
180
180
  allowed_cidrs = [cidr.strip() for cidr in allow_cidrs_str.split(',') if cidr.strip()]
@@ -188,35 +188,35 @@ def lambda_handler(event, context):
188
188
  print(f"IP {client_ip} is allowed")
189
189
  return request
190
190
 
191
- # IP not allowed - either redirect or proxy maintenance page
191
+ # IP not allowed - either redirect or proxy backup page
192
192
  # Check response mode from SSM (default: redirect for backward compatibility)
193
193
  response_mode_param = f"/{env}/{function_name}/response-mode"
194
194
  response_mode = get_ssm_parameter(response_mode_param, 'us-east-1', default='redirect')
195
195
 
196
196
  if response_mode == 'proxy':
197
- # Proxy mode: Fetch and return maintenance content (keeps URL the same)
198
- print(f"IP {client_ip} is NOT allowed, proxying content from {maint_cf_host}")
197
+ # Proxy mode: Fetch and return backup content (keeps URL the same)
198
+ print(f"IP {client_ip} is NOT allowed, proxying content from {dns_alias}")
199
199
 
200
200
  try:
201
201
  import urllib3
202
202
  http = urllib3.PoolManager()
203
203
 
204
- # Fetch the maintenance page
205
- maint_response = http.request(
204
+ # Fetch the backup page - always request /index.html
205
+ alias_response = http.request(
206
206
  'GET',
207
- f'https://{maint_cf_host}',
207
+ f'https://{dns_alias}/index.html',
208
208
  headers={'User-Agent': 'CloudFront-IP-Gate-Proxy'},
209
209
  timeout=3.0
210
210
  )
211
211
 
212
- # Return the maintenance content
212
+ # Return the backup content
213
213
  response = {
214
- 'status': str(maint_response.status),
215
- 'statusDescription': 'OK' if maint_response.status == 200 else 'Service Unavailable',
214
+ 'status': str(alias_response.status),
215
+ 'statusDescription': 'OK' if alias_response.status == 200 else 'Service Unavailable',
216
216
  'headers': {
217
217
  'content-type': [{
218
218
  'key': 'Content-Type',
219
- 'value': maint_response.headers.get('Content-Type', 'text/html')
219
+ 'value': alias_response.headers.get('Content-Type', 'text/html')
220
220
  }],
221
221
  'cache-control': [{
222
222
  'key': 'Cache-Control',
@@ -227,20 +227,20 @@ def lambda_handler(event, context):
227
227
  'value': 'proxy'
228
228
  }]
229
229
  },
230
- 'body': maint_response.data.decode('utf-8')
230
+ 'body': alias_response.data.decode('utf-8')
231
231
  }
232
232
 
233
- print(f"Successfully proxied maintenance page (status {maint_response.status})")
233
+ print(f"Successfully proxied backup page (status {alias_response.status})")
234
234
  return response
235
235
 
236
236
  except Exception as proxy_error:
237
- print(f"Error proxying maintenance content: {str(proxy_error)}")
237
+ print(f"Error proxying backup content: {str(proxy_error)}")
238
238
  # Fall back to redirect if proxy fails
239
239
  print(f"Falling back to redirect mode")
240
240
  response_mode = 'redirect'
241
241
 
242
- # Redirect mode (default): HTTP 302 redirect to maintenance site
243
- print(f"IP {client_ip} is NOT allowed, redirecting to {maint_cf_host}")
242
+ # Redirect mode (default): HTTP 302 redirect to backup site
243
+ print(f"IP {client_ip} is NOT allowed, redirecting to {dns_alias}")
244
244
 
245
245
  response = {
246
246
  'status': '302',
@@ -248,7 +248,7 @@ def lambda_handler(event, context):
248
248
  'headers': {
249
249
  'location': [{
250
250
  'key': 'Location',
251
- 'value': f'https://{maint_cf_host}'
251
+ 'value': f'https://{dns_alias}'
252
252
  }],
253
253
  'cache-control': [{
254
254
  'key': 'Cache-Control',
@@ -225,6 +225,18 @@ class SecurityGroupsStack(IStack):
225
225
  export_name=f"{self.deployment.environment}-{self.workload.name}-WebMonitoringSecurityGroup",
226
226
  )
227
227
 
228
+ # =========================================================
229
+ # SSM Parameter Store Exports
230
+ # =========================================================
231
+ self._export_ssm_parameters(
232
+ security_groups_map={
233
+ "alb": alb_sg,
234
+ "ecs": web_fleet_sg,
235
+ "rds": mysql_sg,
236
+ "monitoring": monitoring_sg,
237
+ }
238
+ )
239
+
228
240
  def _process_ssm_imports(self) -> None:
229
241
  """
230
242
  Process SSM imports from configuration.
@@ -287,3 +299,64 @@ class SecurityGroupsStack(IStack):
287
299
  raise ValueError("VPC ID is not defined in the configuration or SSM imports.")
288
300
 
289
301
  return self._vpc
302
+
303
+ def _export_ssm_parameters(self, security_groups_map: Dict[str, ec2.CfnSecurityGroup]) -> None:
304
+ """
305
+ Export security group IDs to SSM Parameter Store based on configuration.
306
+
307
+ Args:
308
+ security_groups_map: Dictionary mapping security group types to their CDK resources
309
+ """
310
+ # Get the security groups configuration list from the config
311
+ security_groups_config = self.sg_config.security_groups
312
+
313
+ if not security_groups_config:
314
+ logger.debug("No security groups configuration found for SSM exports")
315
+ return
316
+
317
+ logger.info(f"Processing SSM exports for {len(security_groups_config)} security groups")
318
+
319
+ # Process each security group configuration
320
+ for sg_config in security_groups_config:
321
+ # Get the security group name and SSM exports
322
+ sg_name = sg_config.get("name", "")
323
+ ssm_config = sg_config.get("ssm", {})
324
+ ssm_exports = ssm_config.get("exports", {})
325
+
326
+ if not ssm_exports:
327
+ logger.debug(f"No SSM exports configured for security group: {sg_name}")
328
+ continue
329
+
330
+ # Determine which security group this config refers to based on the name pattern
331
+ # The config uses patterns like "{{WORKLOAD_NAME}}-{{ENVIRONMENT}}-rds-sg"
332
+ sg_resource = None
333
+ sg_type = None
334
+
335
+ if "-rds-sg" in sg_name or "-rds" in sg_name:
336
+ sg_resource = security_groups_map.get("rds")
337
+ sg_type = "rds"
338
+ elif "-ecs-sg" in sg_name or "instances" in sg_name:
339
+ sg_resource = security_groups_map.get("ecs")
340
+ sg_type = "ecs"
341
+ elif "-alb-sg" in sg_name or "alb" in sg_name:
342
+ sg_resource = security_groups_map.get("alb")
343
+ sg_type = "alb"
344
+ elif "monitoring" in sg_name:
345
+ sg_resource = security_groups_map.get("monitoring")
346
+ sg_type = "monitoring"
347
+
348
+ if not sg_resource:
349
+ logger.warning(f"Could not map security group configuration to resource: {sg_name}")
350
+ continue
351
+
352
+ # Export the security group ID if configured
353
+ security_group_id_path = ssm_exports.get("security_group_id")
354
+ if security_group_id_path:
355
+ self.export_ssm_parameter(
356
+ scope=self,
357
+ id=f"SsmExport{sg_type.upper()}SecurityGroupId",
358
+ value=sg_resource.ref,
359
+ parameter_name=security_group_id_path,
360
+ description=f"Security Group ID for {sg_type} ({sg_name})",
361
+ )
362
+ logger.info(f"Exported SSM parameter: {security_group_id_path} for {sg_type} security group")
@@ -261,6 +261,18 @@ class StaticWebSiteStack(IStack):
261
261
  description=f"CloudFront distribution ID for {stack_config.name}",
262
262
  )
263
263
 
264
+ # Export DNS alias (first alias) if configured
265
+ if "dns_alias" in ssm_exports and cloudfront_distribution.aliases:
266
+ # Export the first alias (primary domain)
267
+ primary_alias = cloudfront_distribution.aliases[0] if isinstance(cloudfront_distribution.aliases, list) else cloudfront_distribution.aliases
268
+ self.export_ssm_parameter(
269
+ scope=self,
270
+ id="SsmExportDnsAlias",
271
+ value=primary_alias,
272
+ parameter_name=ssm_exports["dns_alias"],
273
+ description=f"Primary DNS alias for {stack_config.name}",
274
+ )
275
+
264
276
  logger.info(f"Exported {len(ssm_exports)} SSM parameters for stack {stack_config.name}")
265
277
 
266
278
  def __get_version_number(self, assets_path: str) -> str:
@@ -1425,7 +1425,9 @@ class ApiGatewayIntegrationUtility:
1425
1425
  f" 2. Add 'allow_public_override': true to explicitly allow public access\n"
1426
1426
  f" 3. Remove 'authorization_type': 'NONE' to use secure Cognito auth\n\n"
1427
1427
  f"🔒 This prevents accidental public endpoints when authentication is available.\n\n"
1428
- f"👉 ApiGatewayIntegrationUtility documentation for more details: https://github.com/your-repo/api-gateway-stack"
1428
+ f"👉 ApiGatewayIntegrationUtility documentation for more details: \n\n "
1429
+ "\t https://github.com/geekcafe/cdk-factory/blob/main/src/cdk_factory/utilities/api_gateway_integration_utility.py \n\n"
1430
+ "\t and https://github.com/geekcafe/cdk-factory/blob/main/src/cdk_factory/stack_library/api_gateway/api_gateway_stack.py"
1429
1431
  )
1430
1432
  raise ValueError(error_msg)
1431
1433
 
cdk_factory/version.py CHANGED
@@ -1 +1 @@
1
- __version__ = "0.15.7"
1
+ __version__ = "0.15.9"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cdk_factory
3
- Version: 0.15.7
3
+ Version: 0.15.9
4
4
  Summary: CDK Factory. A QuickStarter and best practices setup for CDK projects
5
5
  Author-email: Eric Wilson <eric.wilson@geekcafe.com>
6
6
  License: MIT License
@@ -2,7 +2,7 @@ cdk_factory/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
2
  cdk_factory/app.py,sha256=RnX0-pwdTAPAdKJK_j13Zl8anf9zYKBwboR0KA8K8xM,10346
3
3
  cdk_factory/cdk.json,sha256=SKZKhJ2PBpFH78j-F8S3VDYW-lf76--Q2I3ON-ZIQfw,3106
4
4
  cdk_factory/cli.py,sha256=FGbCTS5dYCNsfp-etshzvFlGDCjC28r6rtzYbe7KoHI,6407
5
- cdk_factory/version.py,sha256=MAPfkctPEvU6tygJmfGoOx7ZrEBKkfDrKqpCja4D-yM,23
5
+ cdk_factory/version.py,sha256=k-GJzgMV-c2ATHbR4wgw0IKwlkANiQ4VvTZ6xMxt9UA,23
6
6
  cdk_factory/builds/README.md,sha256=9BBWd7bXpyKdMU_g2UljhQwrC9i5O_Tvkb6oPvndoZk,90
7
7
  cdk_factory/commands/command_loader.py,sha256=QbLquuP_AdxtlxlDy-2IWCQ6D-7qa58aphnDPtp_uTs,3744
8
8
  cdk_factory/configurations/base_config.py,sha256=JKjhNsy0RCUZy1s8n5D_aXXI-upR9izaLtCTfKYiV9k,9624
@@ -47,7 +47,7 @@ cdk_factory/configurations/resources/route53_hosted_zone.py,sha256=qjEYPCSxSOx5b
47
47
  cdk_factory/configurations/resources/rum.py,sha256=5aNLhyJEl97spby2gEV59RsMIQpUto2hGh1DeSyIp_I,5149
48
48
  cdk_factory/configurations/resources/s3.py,sha256=LBwTOZ4tOxNbgiu1fFGHOTyF5jlzeVphc_9VAqNw8zA,6042
49
49
  cdk_factory/configurations/resources/security_group.py,sha256=8kQtaaRVEn2aDm8XoC7QFh2mDOFbPbgobmssIuqU8MA,2259
50
- cdk_factory/configurations/resources/security_group_full_stack.py,sha256=3LHIJw4BEsagb0EMnc8C2_g4OQxUe0kQTn3XqQltUIE,2402
50
+ cdk_factory/configurations/resources/security_group_full_stack.py,sha256=OzcQF5UMqIVAtDAlGY1kf1IWxq2x_gmnwAUTIZ5ITzY,2947
51
51
  cdk_factory/configurations/resources/sqs.py,sha256=fAh2dqttJ6PX46enFRULuiLEu3TEj0Vb2xntAOgUpYE,4346
52
52
  cdk_factory/configurations/resources/vpc.py,sha256=sNn6w76bHFwmt6N76gZZhqpsuNB9860C1SZu6tebaXY,3835
53
53
  cdk_factory/constructs/cloudfront/cloudfront_distribution_construct.py,sha256=LHgjvTNghCMTpHh90VWl7AbE100Er-S9EgyEVt12J_c,25809
@@ -66,7 +66,7 @@ cdk_factory/interfaces/istack.py,sha256=bhTBs-o9FgKwvJMSuwxjUV6D3nUlvZHVzfm27jP9
66
66
  cdk_factory/interfaces/live_ssm_resolver.py,sha256=3FIr9a02SXqZmbFs3RT0WxczWEQR_CF7QSt7kWbDrVE,8163
67
67
  cdk_factory/interfaces/ssm_parameter_mixin.py,sha256=uA2j8HmAOpuEA9ynRj51s0WjUHMVLsbLQN-QS9NKyHA,12089
68
68
  cdk_factory/lambdas/health_handler.py,sha256=dd40ykKMxWCFEIyp2ZdQvAGNjw_ylI9CSm1N24Hp2ME,196
69
- cdk_factory/lambdas/edge/ip_gate/handler.py,sha256=5uSpCB8Kii1CDqgb_Gv8EvVeSzA5XtynaNL3t7tXf_M,10507
69
+ cdk_factory/lambdas/edge/ip_gate/handler.py,sha256=YrXO42gEhJoBTaH6jS7EWqjHe9t5Fpt4WLgY8vjtou0,10474
70
70
  cdk_factory/pipeline/path_utils.py,sha256=fvWdrcb4onmpIu1APkHLhXg8zWfK74HcW3Ra2ynxfXM,2586
71
71
  cdk_factory/pipeline/pipeline_factory.py,sha256=rvtkdlTPJG477nTVRN8S2ksWt4bwpd9eVLFd9WO02pM,17248
72
72
  cdk_factory/pipeline/stage.py,sha256=Be7ExMB9A-linRM18IQDOzQ-cP_I2_ThRNzlT4FIrUg,437
@@ -107,17 +107,17 @@ cdk_factory/stack_library/route53/route53_stack.py,sha256=R-6DW7gIjeg25uBT5ZMLND
107
107
  cdk_factory/stack_library/rum/__init__.py,sha256=gUrWQdzd4rZ2J0YzAQC8PsEGAS7QgyYjB2ZCUKWasy4,90
108
108
  cdk_factory/stack_library/rum/rum_stack.py,sha256=OvQ6tsjYcXS8adqU_Xh0A_VKdnPtQnij4cG67nNqSVo,13611
109
109
  cdk_factory/stack_library/security_group/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
110
- cdk_factory/stack_library/security_group/security_group_full_stack.py,sha256=zu-xrz2KuojJGoN4-sTzD14sT9DwYaJpgwFl3wPiNXw,10907
110
+ cdk_factory/stack_library/security_group/security_group_full_stack.py,sha256=je6MF6lriSaOMtIZ9hrcFagXObu5SItccpBr9Mp5TMo,14200
111
111
  cdk_factory/stack_library/security_group/security_group_stack.py,sha256=2zxd5ozgQ4GP0xi-Ni7SyChtEAOzC0nXeGz78DPXwPg,14445
112
112
  cdk_factory/stack_library/simple_queue_service/sqs_stack.py,sha256=jJksWrvrvgZUMM01RZ317DOIxqIJbkYYSYu38w0jHpc,6039
113
113
  cdk_factory/stack_library/vpc/__init__.py,sha256=7pIqP97Gf2AJbv9Ebp1WbQGHYhgEbWJ52L1MzeXBybA,42
114
114
  cdk_factory/stack_library/vpc/vpc_stack.py,sha256=UZuzb5uSOi4ghuLGPvsKqc3gwe6XI89jHV4WHX8MelA,11472
115
- cdk_factory/stack_library/websites/static_website_stack.py,sha256=XtrqJaMnrs1XvSz5-8LFaohtY68mtprIOrrizyjnS0w,10608
115
+ cdk_factory/stack_library/websites/static_website_stack.py,sha256=A292BlKDof0JnVewkK_3JiRB04rX7J9Na0a-iz3JWzw,11243
116
116
  cdk_factory/stages/websites/static_website_stage.py,sha256=X4fpKXkhb0zIbSHx3QyddBhVSLBryb1vf1Cg2fMTqog,755
117
117
  cdk_factory/templates/README.md,sha256=ATBEjG6beYvbEAdLtZ_8xnxgFD5X0cgZoI_6pToqH90,2679
118
118
  cdk_factory/templates/app.py.template,sha256=aM60x0nNV80idtCL8jm1EddY63F5tDITYOlavg-BPMU,1069
119
119
  cdk_factory/templates/cdk.json.template,sha256=SuGz4Y6kCVMDRpJrA_AJlp0kwdENiJPVngIv1xP5bwI,3526
120
- cdk_factory/utilities/api_gateway_integration_utility.py,sha256=yblKiMIHGXqKb7JK5IbzGM_TXjX9j893BMqgqBT44DE,63449
120
+ cdk_factory/utilities/api_gateway_integration_utility.py,sha256=Mpyq038ZvL8FafcOwqp2Jj0cNcD_nLfVyTBzMYaUskM,63683
121
121
  cdk_factory/utilities/commandline_args.py,sha256=0FiNEJFbWVN8Ct7r0VHnJEx7rhUlaRKT7R7HMNJBSTI,2216
122
122
  cdk_factory/utilities/configuration_loader.py,sha256=z0ZdGLNbTO4_yfluB9zUh_i_Poc9qj-7oRyjMRlNkN8,1522
123
123
  cdk_factory/utilities/docker_utilities.py,sha256=6ee9KEGsaRJWo6FqvdPtE3_L2Emp3Lc0vu2Ie3VoflI,8280
@@ -129,8 +129,8 @@ cdk_factory/utilities/lambda_function_utilities.py,sha256=S1GvBsY_q2cyUiaud3HORJ
129
129
  cdk_factory/utilities/os_execute.py,sha256=5Op0LY_8Y-pUm04y1k8MTpNrmQvcLmQHPQITEP7EuSU,1019
130
130
  cdk_factory/utils/api_gateway_utilities.py,sha256=If7Xu5s_UxmuV-kL3JkXxPLBdSVUKoLtohm0IUFoiV8,4378
131
131
  cdk_factory/workload/workload_factory.py,sha256=mM8GU_5mKq_0OyK060T3JrUSUiGAcKf0eqNlT9mfaws,6028
132
- cdk_factory-0.15.7.dist-info/METADATA,sha256=QrtFEH2ROwve6PqWxq6av8hH-oCohgCU0is2F-R2C70,2451
133
- cdk_factory-0.15.7.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
134
- cdk_factory-0.15.7.dist-info/entry_points.txt,sha256=S1DPe0ORcdiwEALMN_WIo3UQrW_g4YdQCLEsc_b0Swg,53
135
- cdk_factory-0.15.7.dist-info/licenses/LICENSE,sha256=NOtdOeLwg2il_XBJdXUPFPX8JlV4dqTdDGAd2-khxT8,1066
136
- cdk_factory-0.15.7.dist-info/RECORD,,
132
+ cdk_factory-0.15.9.dist-info/METADATA,sha256=3kgTXVxsXzMHhVRR_MSHhTriF9lhRLywzqssUwiThP4,2451
133
+ cdk_factory-0.15.9.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
134
+ cdk_factory-0.15.9.dist-info/entry_points.txt,sha256=S1DPe0ORcdiwEALMN_WIo3UQrW_g4YdQCLEsc_b0Swg,53
135
+ cdk_factory-0.15.9.dist-info/licenses/LICENSE,sha256=NOtdOeLwg2il_XBJdXUPFPX8JlV4dqTdDGAd2-khxT8,1066
136
+ cdk_factory-0.15.9.dist-info/RECORD,,