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
|
@@ -17,7 +17,7 @@ from cdk_factory.configurations.deployment import DeploymentConfig
|
|
|
17
17
|
from cdk_factory.configurations.resources.cognito import CognitoConfig
|
|
18
18
|
from cdk_factory.configurations.stack import StackConfig
|
|
19
19
|
from cdk_factory.interfaces.istack import IStack
|
|
20
|
-
from cdk_factory.interfaces.
|
|
20
|
+
from cdk_factory.interfaces.standardized_ssm_mixin import StandardizedSsmMixin
|
|
21
21
|
from cdk_factory.stack.stack_module_registry import register_stack
|
|
22
22
|
from cdk_factory.workload.workload_factory import WorkloadConfig
|
|
23
23
|
|
|
@@ -26,7 +26,7 @@ logger = Logger(__name__)
|
|
|
26
26
|
|
|
27
27
|
@register_stack("cognito_library_module")
|
|
28
28
|
@register_stack("cognito_stack")
|
|
29
|
-
class CognitoStack(IStack,
|
|
29
|
+
class CognitoStack(IStack, StandardizedSsmMixin):
|
|
30
30
|
"""
|
|
31
31
|
Cognito Stack - Creates a Cognito User Pool with configurable settings.
|
|
32
32
|
"""
|
|
@@ -40,6 +40,14 @@ class CognitoStack(IStack, EnhancedSsmParameterMixin):
|
|
|
40
40
|
self.user_pool: cognito.UserPool | None = None
|
|
41
41
|
self.app_clients: dict = {} # Store created app clients by name
|
|
42
42
|
|
|
43
|
+
def _build_resource_name(self, name: str) -> str:
|
|
44
|
+
"""Build resource name using deployment configuration"""
|
|
45
|
+
if self.deployment:
|
|
46
|
+
return self.deployment.build_resource_name(name)
|
|
47
|
+
else:
|
|
48
|
+
# Fallback naming pattern
|
|
49
|
+
return f"{self.cognito_config.user_pool_name or 'cognito'}-{name}"
|
|
50
|
+
|
|
43
51
|
def build(
|
|
44
52
|
self,
|
|
45
53
|
stack_config: StackConfig,
|
|
@@ -50,10 +58,10 @@ class CognitoStack(IStack, EnhancedSsmParameterMixin):
|
|
|
50
58
|
self.stack_config = stack_config
|
|
51
59
|
self.deployment = deployment
|
|
52
60
|
self.cognito_config = CognitoConfig(stack_config.dictionary.get("cognito", {}))
|
|
53
|
-
|
|
61
|
+
|
|
54
62
|
# Create user pool with configuration
|
|
55
63
|
self._create_user_pool_with_config()
|
|
56
|
-
|
|
64
|
+
|
|
57
65
|
# Create app clients if configured
|
|
58
66
|
if self.cognito_config.app_clients:
|
|
59
67
|
self._create_app_clients()
|
|
@@ -151,7 +159,7 @@ class CognitoStack(IStack, EnhancedSsmParameterMixin):
|
|
|
151
159
|
|
|
152
160
|
self.user_pool = cognito.UserPool(
|
|
153
161
|
self,
|
|
154
|
-
id=self.
|
|
162
|
+
id=self._build_resource_name(
|
|
155
163
|
self.cognito_config.user_pool_name
|
|
156
164
|
or self.cognito_config.user_pool_id
|
|
157
165
|
or "user-pool"
|
|
@@ -166,21 +174,21 @@ class CognitoStack(IStack, EnhancedSsmParameterMixin):
|
|
|
166
174
|
"""Create app clients for the user pool based on configuration"""
|
|
167
175
|
if not self.user_pool:
|
|
168
176
|
raise ValueError("User pool must be created before app clients")
|
|
169
|
-
|
|
177
|
+
|
|
170
178
|
for client_config in self.cognito_config.app_clients:
|
|
171
179
|
client_name = client_config.get("name")
|
|
172
180
|
if not client_name:
|
|
173
181
|
raise ValueError("App client name is required")
|
|
174
|
-
|
|
182
|
+
|
|
175
183
|
# Build authentication flows
|
|
176
184
|
auth_flows = self._build_auth_flows(client_config.get("auth_flows", {}))
|
|
177
|
-
|
|
185
|
+
|
|
178
186
|
# Build OAuth settings
|
|
179
187
|
oauth_settings = self._build_oauth_settings(client_config.get("oauth"))
|
|
180
|
-
|
|
188
|
+
|
|
181
189
|
# Build token validity settings
|
|
182
190
|
token_validity = self._build_token_validity(client_config)
|
|
183
|
-
|
|
191
|
+
|
|
184
192
|
# Build app client kwargs
|
|
185
193
|
client_kwargs = {
|
|
186
194
|
"user_pool": self.user_pool,
|
|
@@ -188,68 +196,78 @@ class CognitoStack(IStack, EnhancedSsmParameterMixin):
|
|
|
188
196
|
"generate_secret": client_config.get("generate_secret", False),
|
|
189
197
|
"auth_flows": auth_flows,
|
|
190
198
|
"o_auth": oauth_settings,
|
|
191
|
-
"prevent_user_existence_errors": client_config.get(
|
|
192
|
-
|
|
199
|
+
"prevent_user_existence_errors": client_config.get(
|
|
200
|
+
"prevent_user_existence_errors"
|
|
201
|
+
),
|
|
202
|
+
"enable_token_revocation": client_config.get(
|
|
203
|
+
"enable_token_revocation", True
|
|
204
|
+
),
|
|
193
205
|
"access_token_validity": token_validity.get("access_token"),
|
|
194
206
|
"id_token_validity": token_validity.get("id_token"),
|
|
195
207
|
"refresh_token_validity": token_validity.get("refresh_token"),
|
|
196
|
-
"read_attributes": self._build_attributes(
|
|
197
|
-
|
|
208
|
+
"read_attributes": self._build_attributes(
|
|
209
|
+
client_config.get("read_attributes")
|
|
210
|
+
),
|
|
211
|
+
"write_attributes": self._build_attributes(
|
|
212
|
+
client_config.get("write_attributes")
|
|
213
|
+
),
|
|
198
214
|
"supported_identity_providers": self._build_identity_providers(
|
|
199
215
|
client_config.get("supported_identity_providers")
|
|
200
216
|
),
|
|
201
217
|
}
|
|
202
|
-
|
|
218
|
+
|
|
203
219
|
# Remove None values
|
|
204
220
|
client_kwargs = {k: v for k, v in client_kwargs.items() if v is not None}
|
|
205
|
-
|
|
221
|
+
|
|
206
222
|
# Create the app client
|
|
207
223
|
app_client = cognito.UserPoolClient(
|
|
208
224
|
self,
|
|
209
|
-
id=self.
|
|
225
|
+
id=self._build_resource_name(f"{client_name}-client"),
|
|
210
226
|
**client_kwargs,
|
|
211
227
|
)
|
|
212
|
-
|
|
228
|
+
|
|
213
229
|
# Store reference
|
|
214
230
|
self.app_clients[client_name] = app_client
|
|
215
231
|
logger.info(f"Created Cognito App Client: {client_name}")
|
|
216
|
-
|
|
232
|
+
|
|
217
233
|
# Store client secret in Secrets Manager if generated
|
|
218
234
|
if client_config.get("generate_secret", False):
|
|
219
235
|
self._store_client_secret_in_secrets_manager(
|
|
220
236
|
client_name, app_client, self.user_pool
|
|
221
237
|
)
|
|
222
|
-
|
|
238
|
+
|
|
223
239
|
def _build_auth_flows(self, auth_flows_config: dict) -> cognito.AuthFlow:
|
|
224
240
|
"""
|
|
225
241
|
Build authentication flows from configuration.
|
|
226
|
-
|
|
242
|
+
|
|
227
243
|
Note: CDK automatically adds ALLOW_REFRESH_TOKEN_AUTH to all app clients,
|
|
228
244
|
which is required for token refresh functionality.
|
|
229
245
|
"""
|
|
230
246
|
if not auth_flows_config:
|
|
231
247
|
return None
|
|
232
|
-
|
|
248
|
+
|
|
233
249
|
return cognito.AuthFlow(
|
|
234
250
|
user_password=auth_flows_config.get("user_password", False),
|
|
235
251
|
user_srp=auth_flows_config.get("user_srp", False),
|
|
236
252
|
custom=auth_flows_config.get("custom", False),
|
|
237
253
|
admin_user_password=auth_flows_config.get("admin_user_password", False),
|
|
238
254
|
)
|
|
239
|
-
|
|
255
|
+
|
|
240
256
|
def _build_oauth_settings(self, oauth_config: dict) -> cognito.OAuthSettings:
|
|
241
257
|
"""Build OAuth settings from configuration"""
|
|
242
258
|
if not oauth_config:
|
|
243
259
|
return None
|
|
244
|
-
|
|
260
|
+
|
|
245
261
|
# Build OAuth flows
|
|
246
262
|
flows_config = oauth_config.get("flows", {})
|
|
247
263
|
flows = cognito.OAuthFlows(
|
|
248
|
-
authorization_code_grant=flows_config.get(
|
|
264
|
+
authorization_code_grant=flows_config.get(
|
|
265
|
+
"authorization_code_grant", False
|
|
266
|
+
),
|
|
249
267
|
implicit_code_grant=flows_config.get("implicit_code_grant", False),
|
|
250
268
|
client_credentials=flows_config.get("client_credentials", False),
|
|
251
269
|
)
|
|
252
|
-
|
|
270
|
+
|
|
253
271
|
# Build OAuth scopes
|
|
254
272
|
scopes = []
|
|
255
273
|
scope_list = oauth_config.get("scopes", [])
|
|
@@ -267,18 +285,18 @@ class CognitoStack(IStack, EnhancedSsmParameterMixin):
|
|
|
267
285
|
else:
|
|
268
286
|
# Custom scope
|
|
269
287
|
scopes.append(cognito.OAuthScope.custom(scope))
|
|
270
|
-
|
|
288
|
+
|
|
271
289
|
return cognito.OAuthSettings(
|
|
272
290
|
flows=flows,
|
|
273
291
|
scopes=scopes if scopes else None,
|
|
274
292
|
callback_urls=oauth_config.get("callback_urls"),
|
|
275
293
|
logout_urls=oauth_config.get("logout_urls"),
|
|
276
294
|
)
|
|
277
|
-
|
|
295
|
+
|
|
278
296
|
def _build_token_validity(self, client_config: dict) -> dict:
|
|
279
297
|
"""Build token validity settings from configuration"""
|
|
280
298
|
result = {}
|
|
281
|
-
|
|
299
|
+
|
|
282
300
|
# Access token validity
|
|
283
301
|
if "access_token_validity" in client_config:
|
|
284
302
|
validity = client_config["access_token_validity"]
|
|
@@ -288,7 +306,7 @@ class CognitoStack(IStack, EnhancedSsmParameterMixin):
|
|
|
288
306
|
result["access_token"] = cdk.Duration.hours(validity["hours"])
|
|
289
307
|
elif "days" in validity:
|
|
290
308
|
result["access_token"] = cdk.Duration.days(validity["days"])
|
|
291
|
-
|
|
309
|
+
|
|
292
310
|
# ID token validity
|
|
293
311
|
if "id_token_validity" in client_config:
|
|
294
312
|
validity = client_config["id_token_validity"]
|
|
@@ -298,7 +316,7 @@ class CognitoStack(IStack, EnhancedSsmParameterMixin):
|
|
|
298
316
|
result["id_token"] = cdk.Duration.hours(validity["hours"])
|
|
299
317
|
elif "days" in validity:
|
|
300
318
|
result["id_token"] = cdk.Duration.days(validity["days"])
|
|
301
|
-
|
|
319
|
+
|
|
302
320
|
# Refresh token validity
|
|
303
321
|
if "refresh_token_validity" in client_config:
|
|
304
322
|
validity = client_config["refresh_token_validity"]
|
|
@@ -308,51 +326,89 @@ class CognitoStack(IStack, EnhancedSsmParameterMixin):
|
|
|
308
326
|
result["refresh_token"] = cdk.Duration.hours(validity["hours"])
|
|
309
327
|
elif "days" in validity:
|
|
310
328
|
result["refresh_token"] = cdk.Duration.days(validity["days"])
|
|
311
|
-
|
|
329
|
+
|
|
312
330
|
return result
|
|
313
|
-
|
|
331
|
+
|
|
314
332
|
def _build_attributes(self, attribute_list: list) -> cognito.ClientAttributes:
|
|
315
333
|
"""Build client attributes from configuration"""
|
|
316
334
|
if not attribute_list:
|
|
317
335
|
return None
|
|
318
|
-
|
|
336
|
+
|
|
319
337
|
# Standard attributes mapping
|
|
320
338
|
standard_attrs = {
|
|
321
|
-
"address": lambda: cognito.ClientAttributes().with_standard_attributes(
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
"
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
"
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
"
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
"
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
"
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
"
|
|
339
|
+
"address": lambda: cognito.ClientAttributes().with_standard_attributes(
|
|
340
|
+
address=True
|
|
341
|
+
),
|
|
342
|
+
"birthdate": lambda: cognito.ClientAttributes().with_standard_attributes(
|
|
343
|
+
birthdate=True
|
|
344
|
+
),
|
|
345
|
+
"email": lambda: cognito.ClientAttributes().with_standard_attributes(
|
|
346
|
+
email=True
|
|
347
|
+
),
|
|
348
|
+
"email_verified": lambda: cognito.ClientAttributes().with_standard_attributes(
|
|
349
|
+
email_verified=True
|
|
350
|
+
),
|
|
351
|
+
"family_name": lambda: cognito.ClientAttributes().with_standard_attributes(
|
|
352
|
+
family_name=True
|
|
353
|
+
),
|
|
354
|
+
"gender": lambda: cognito.ClientAttributes().with_standard_attributes(
|
|
355
|
+
gender=True
|
|
356
|
+
),
|
|
357
|
+
"given_name": lambda: cognito.ClientAttributes().with_standard_attributes(
|
|
358
|
+
given_name=True
|
|
359
|
+
),
|
|
360
|
+
"locale": lambda: cognito.ClientAttributes().with_standard_attributes(
|
|
361
|
+
locale=True
|
|
362
|
+
),
|
|
363
|
+
"middle_name": lambda: cognito.ClientAttributes().with_standard_attributes(
|
|
364
|
+
middle_name=True
|
|
365
|
+
),
|
|
366
|
+
"name": lambda: cognito.ClientAttributes().with_standard_attributes(
|
|
367
|
+
fullname=True
|
|
368
|
+
),
|
|
369
|
+
"nickname": lambda: cognito.ClientAttributes().with_standard_attributes(
|
|
370
|
+
nickname=True
|
|
371
|
+
),
|
|
372
|
+
"phone_number": lambda: cognito.ClientAttributes().with_standard_attributes(
|
|
373
|
+
phone_number=True
|
|
374
|
+
),
|
|
375
|
+
"phone_number_verified": lambda: cognito.ClientAttributes().with_standard_attributes(
|
|
376
|
+
phone_number_verified=True
|
|
377
|
+
),
|
|
378
|
+
"picture": lambda: cognito.ClientAttributes().with_standard_attributes(
|
|
379
|
+
picture=True
|
|
380
|
+
),
|
|
381
|
+
"preferred_username": lambda: cognito.ClientAttributes().with_standard_attributes(
|
|
382
|
+
preferred_username=True
|
|
383
|
+
),
|
|
384
|
+
"profile": lambda: cognito.ClientAttributes().with_standard_attributes(
|
|
385
|
+
profile=True
|
|
386
|
+
),
|
|
387
|
+
"timezone": lambda: cognito.ClientAttributes().with_standard_attributes(
|
|
388
|
+
timezone=True
|
|
389
|
+
),
|
|
390
|
+
"updated_at": lambda: cognito.ClientAttributes().with_standard_attributes(
|
|
391
|
+
last_update_time=True
|
|
392
|
+
),
|
|
393
|
+
"website": lambda: cognito.ClientAttributes().with_standard_attributes(
|
|
394
|
+
website=True
|
|
395
|
+
),
|
|
340
396
|
}
|
|
341
|
-
|
|
397
|
+
|
|
342
398
|
# Start with empty attributes
|
|
343
399
|
attrs = cognito.ClientAttributes()
|
|
344
|
-
|
|
400
|
+
|
|
345
401
|
# Build standard attributes
|
|
346
402
|
standard_dict = {}
|
|
347
403
|
custom_list = []
|
|
348
|
-
|
|
404
|
+
|
|
349
405
|
for attr in attribute_list:
|
|
350
406
|
if attr in standard_attrs:
|
|
351
407
|
standard_dict[attr] = True
|
|
352
408
|
else:
|
|
353
409
|
# Custom attribute
|
|
354
410
|
custom_list.append(attr)
|
|
355
|
-
|
|
411
|
+
|
|
356
412
|
# Apply standard attributes if any
|
|
357
413
|
if standard_dict:
|
|
358
414
|
# Map attribute names to CDK parameter names
|
|
@@ -377,22 +433,22 @@ class CognitoStack(IStack, EnhancedSsmParameterMixin):
|
|
|
377
433
|
"updated_at": "last_update_time",
|
|
378
434
|
"website": "website",
|
|
379
435
|
}
|
|
380
|
-
|
|
436
|
+
|
|
381
437
|
# Convert to CDK parameter names
|
|
382
438
|
cdk_attrs = {attr_mapping.get(k, k): v for k, v in standard_dict.items()}
|
|
383
439
|
attrs = attrs.with_standard_attributes(**cdk_attrs)
|
|
384
|
-
|
|
440
|
+
|
|
385
441
|
# Add custom attributes if any
|
|
386
442
|
if custom_list:
|
|
387
443
|
attrs = attrs.with_custom_attributes(*custom_list)
|
|
388
|
-
|
|
444
|
+
|
|
389
445
|
return attrs
|
|
390
|
-
|
|
446
|
+
|
|
391
447
|
def _build_identity_providers(self, providers: list) -> list:
|
|
392
448
|
"""Build identity provider list from configuration"""
|
|
393
449
|
if not providers:
|
|
394
450
|
return None
|
|
395
|
-
|
|
451
|
+
|
|
396
452
|
result = []
|
|
397
453
|
for provider in providers:
|
|
398
454
|
if isinstance(provider, str):
|
|
@@ -408,15 +464,17 @@ class CognitoStack(IStack, EnhancedSsmParameterMixin):
|
|
|
408
464
|
result.append(cognito.UserPoolClientIdentityProvider.APPLE)
|
|
409
465
|
else:
|
|
410
466
|
# Custom provider
|
|
411
|
-
result.append(
|
|
412
|
-
|
|
467
|
+
result.append(
|
|
468
|
+
cognito.UserPoolClientIdentityProvider.custom(provider)
|
|
469
|
+
)
|
|
470
|
+
|
|
413
471
|
return result if result else None
|
|
414
472
|
|
|
415
473
|
def _store_client_secret_in_secrets_manager(
|
|
416
474
|
self,
|
|
417
475
|
client_name: str,
|
|
418
476
|
app_client: cognito.UserPoolClient,
|
|
419
|
-
user_pool: cognito.UserPool
|
|
477
|
+
user_pool: cognito.UserPool,
|
|
420
478
|
):
|
|
421
479
|
"""
|
|
422
480
|
Store Cognito app client secret in AWS Secrets Manager.
|
|
@@ -438,37 +496,37 @@ class CognitoStack(IStack, EnhancedSsmParameterMixin):
|
|
|
438
496
|
f"{client_name}-secret-{app_client.user_pool_client_id}"
|
|
439
497
|
),
|
|
440
498
|
),
|
|
441
|
-
policy=cr.AwsCustomResourcePolicy.from_statements(
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
499
|
+
policy=cr.AwsCustomResourcePolicy.from_statements(
|
|
500
|
+
[
|
|
501
|
+
iam.PolicyStatement(
|
|
502
|
+
actions=["cognito-idp:DescribeUserPoolClient"],
|
|
503
|
+
resources=[user_pool.user_pool_arn],
|
|
504
|
+
)
|
|
505
|
+
]
|
|
506
|
+
),
|
|
447
507
|
)
|
|
448
|
-
|
|
508
|
+
|
|
449
509
|
# Get the client secret from the custom resource response
|
|
450
510
|
client_secret = get_client_secret.get_response_field(
|
|
451
511
|
"UserPoolClient.ClientSecret"
|
|
452
512
|
)
|
|
453
|
-
|
|
513
|
+
|
|
454
514
|
# Create secret in Secrets Manager
|
|
455
515
|
secret = secretsmanager.Secret(
|
|
456
516
|
self,
|
|
457
517
|
f"{client_name}-client-secret",
|
|
458
|
-
secret_name=self.
|
|
518
|
+
secret_name=self._build_resource_name(
|
|
459
519
|
f"cognito/{client_name}/client-secret"
|
|
460
520
|
),
|
|
461
521
|
description=f"Cognito app client secret for {client_name}",
|
|
462
522
|
secret_string_value=cdk.SecretValue.unsafe_plain_text(client_secret),
|
|
463
523
|
)
|
|
464
|
-
|
|
524
|
+
|
|
465
525
|
# Also store client ID in the same secret for convenience
|
|
466
526
|
secret_with_metadata = secretsmanager.Secret(
|
|
467
527
|
self,
|
|
468
528
|
f"{client_name}-client-credentials",
|
|
469
|
-
secret_name=self.
|
|
470
|
-
f"cognito/{client_name}/credentials"
|
|
471
|
-
),
|
|
529
|
+
secret_name=self._build_resource_name(f"cognito/{client_name}/credentials"),
|
|
472
530
|
description=f"Cognito app client credentials for {client_name}",
|
|
473
531
|
secret_object_value={
|
|
474
532
|
"client_id": cdk.SecretValue.unsafe_plain_text(
|
|
@@ -480,18 +538,18 @@ class CognitoStack(IStack, EnhancedSsmParameterMixin):
|
|
|
480
538
|
),
|
|
481
539
|
},
|
|
482
540
|
)
|
|
483
|
-
|
|
541
|
+
|
|
484
542
|
logger.info(
|
|
485
543
|
f"Stored client secret for {client_name} in Secrets Manager: "
|
|
486
544
|
f"{secret_with_metadata.secret_name}"
|
|
487
545
|
)
|
|
488
|
-
|
|
546
|
+
|
|
489
547
|
# Export secret ARN to SSM for cross-stack reference
|
|
490
548
|
if self.cognito_config.ssm.get("enabled"):
|
|
491
549
|
safe_client_name = client_name.replace("-", "_").replace(" ", "_")
|
|
492
550
|
org = self.cognito_config.ssm.get("organization", "default")
|
|
493
551
|
env = self.cognito_config.ssm.get("environment", "dev")
|
|
494
|
-
|
|
552
|
+
|
|
495
553
|
ssm.StringParameter(
|
|
496
554
|
self,
|
|
497
555
|
f"{client_name}-secret-arn-param",
|
|
@@ -502,37 +560,39 @@ class CognitoStack(IStack, EnhancedSsmParameterMixin):
|
|
|
502
560
|
|
|
503
561
|
def _export_ssm_parameters(self, user_pool: cognito.UserPool):
|
|
504
562
|
"""Export Cognito resources to SSM using enhanced SSM parameter mixin"""
|
|
505
|
-
|
|
563
|
+
|
|
506
564
|
# Setup enhanced SSM integration with proper resource type and name
|
|
507
565
|
# Use "user-pool" as resource identifier for SSM paths, not the full pool name
|
|
508
|
-
|
|
509
|
-
self.
|
|
566
|
+
|
|
567
|
+
self.setup_ssm_integration(
|
|
510
568
|
scope=self,
|
|
511
569
|
config=self.stack_config.dictionary.get("cognito", {}),
|
|
512
570
|
resource_type="cognito",
|
|
513
|
-
resource_name="user-pool"
|
|
571
|
+
resource_name="user-pool",
|
|
514
572
|
)
|
|
515
|
-
|
|
573
|
+
|
|
516
574
|
# Prepare resource values for export
|
|
517
575
|
resource_values = {
|
|
518
576
|
"user_pool_id": user_pool.user_pool_id,
|
|
519
577
|
"user_pool_name": self.cognito_config.user_pool_name,
|
|
520
578
|
"user_pool_arn": user_pool.user_pool_arn,
|
|
521
579
|
}
|
|
522
|
-
|
|
580
|
+
|
|
523
581
|
# Add app client IDs to export
|
|
524
582
|
for client_name, app_client in self.app_clients.items():
|
|
525
583
|
# Export client ID
|
|
526
584
|
safe_client_name = client_name.replace("-", "_").replace(" ", "_")
|
|
527
|
-
resource_values[f"app_client_{safe_client_name}_id"] =
|
|
528
|
-
|
|
585
|
+
resource_values[f"app_client_{safe_client_name}_id"] = (
|
|
586
|
+
app_client.user_pool_client_id
|
|
587
|
+
)
|
|
588
|
+
|
|
529
589
|
# Note: Client secrets cannot be exported via SSM as they are only available
|
|
530
590
|
# at creation time and CDK doesn't expose them. Use AWS Secrets Manager
|
|
531
591
|
# or retrieve via AWS Console/CLI if needed.
|
|
532
|
-
|
|
592
|
+
|
|
533
593
|
# Use enhanced SSM parameter export
|
|
534
|
-
exported_params = self.
|
|
535
|
-
|
|
594
|
+
exported_params = self.export_ssm_parameters(resource_values)
|
|
595
|
+
|
|
536
596
|
if exported_params:
|
|
537
597
|
logger.info(f"Exported {len(exported_params)} Cognito parameters to SSM")
|
|
538
598
|
else:
|
|
@@ -9,7 +9,7 @@ import aws_cdk as cdk
|
|
|
9
9
|
from aws_cdk import aws_dynamodb as dynamodb
|
|
10
10
|
from constructs import Construct
|
|
11
11
|
from cdk_factory.interfaces.istack import IStack
|
|
12
|
-
from cdk_factory.interfaces.
|
|
12
|
+
from cdk_factory.interfaces.standardized_ssm_mixin import StandardizedSsmMixin
|
|
13
13
|
from aws_lambda_powertools import Logger
|
|
14
14
|
from cdk_factory.stack.stack_module_registry import register_stack
|
|
15
15
|
from typing import List, Dict, Any, Optional
|
|
@@ -23,7 +23,7 @@ logger = Logger(service="DynamoDBStack")
|
|
|
23
23
|
|
|
24
24
|
@register_stack("dynamodb_stack")
|
|
25
25
|
@register_stack("dynamodb_library_module")
|
|
26
|
-
class DynamoDBStack(IStack,
|
|
26
|
+
class DynamoDBStack(IStack, StandardizedSsmMixin):
|
|
27
27
|
"""
|
|
28
28
|
Reusable stack for AWS DynamoDB tables.
|
|
29
29
|
Supports all major DynamoDB table parameters.
|
|
@@ -103,7 +103,7 @@ class DynamoDBStack(IStack, EnhancedSsmParameterMixin):
|
|
|
103
103
|
self._configure_gsi()
|
|
104
104
|
# add replicas if configured
|
|
105
105
|
self._configure_replicas()
|
|
106
|
-
|
|
106
|
+
|
|
107
107
|
# Export SSM parameters
|
|
108
108
|
self._export_ssm_parameters()
|
|
109
109
|
|
|
@@ -148,34 +148,38 @@ class DynamoDBStack(IStack, EnhancedSsmParameterMixin):
|
|
|
148
148
|
"""Export DynamoDB resources to SSM using enhanced SSM parameter mixin"""
|
|
149
149
|
if not self.table:
|
|
150
150
|
return
|
|
151
|
-
|
|
151
|
+
|
|
152
152
|
# Setup enhanced SSM integration with proper resource type and name
|
|
153
153
|
# Use "app-table" as resource identifier for SSM paths, not the full table name
|
|
154
|
-
|
|
155
|
-
self.
|
|
154
|
+
|
|
155
|
+
self.setup_ssm_integration(
|
|
156
156
|
scope=self,
|
|
157
157
|
config=self.stack_config.dictionary.get("dynamodb", {}),
|
|
158
158
|
resource_type="dynamodb",
|
|
159
|
-
resource_name="app-table"
|
|
159
|
+
resource_name="app-table",
|
|
160
160
|
)
|
|
161
|
-
|
|
161
|
+
|
|
162
162
|
# Prepare resource values for export
|
|
163
163
|
resource_values = {
|
|
164
164
|
"table_name": self.table.table_name,
|
|
165
165
|
"table_arn": self.table.table_arn,
|
|
166
|
-
"table_stream_arn":
|
|
166
|
+
"table_stream_arn": (
|
|
167
|
+
self.table.table_stream_arn
|
|
168
|
+
if hasattr(self.table, "table_stream_arn")
|
|
169
|
+
else None
|
|
170
|
+
),
|
|
167
171
|
}
|
|
168
|
-
|
|
172
|
+
|
|
169
173
|
# Add GSI names if available
|
|
170
|
-
if hasattr(self,
|
|
174
|
+
if hasattr(self, "_gsi_names") and self._gsi_names:
|
|
171
175
|
resource_values["gsi_names"] = ",".join(self._gsi_names)
|
|
172
|
-
|
|
176
|
+
|
|
173
177
|
# Filter out None values
|
|
174
178
|
resource_values = {k: v for k, v in resource_values.items() if v is not None}
|
|
175
|
-
|
|
179
|
+
|
|
176
180
|
# Use enhanced SSM parameter export
|
|
177
|
-
exported_params = self.
|
|
178
|
-
|
|
181
|
+
exported_params = self.export_ssm_parameters(resource_values)
|
|
182
|
+
|
|
179
183
|
if exported_params:
|
|
180
184
|
logger.info(f"Exported {len(exported_params)} DynamoDB parameters to SSM")
|
|
181
185
|
else:
|
|
@@ -12,7 +12,7 @@ from cdk_factory.configurations.resources.ecr import ECRConfig
|
|
|
12
12
|
from cdk_factory.configurations.stack import StackConfig
|
|
13
13
|
from cdk_factory.constructs.ecr.ecr_construct import ECRConstruct
|
|
14
14
|
from cdk_factory.interfaces.istack import IStack
|
|
15
|
-
from cdk_factory.interfaces.
|
|
15
|
+
from cdk_factory.interfaces.standardized_ssm_mixin import StandardizedSsmMixin
|
|
16
16
|
from cdk_factory.stack.stack_module_registry import register_stack
|
|
17
17
|
from cdk_factory.workload.workload_factory import WorkloadConfig
|
|
18
18
|
|
|
@@ -21,7 +21,7 @@ logger = Logger(__name__)
|
|
|
21
21
|
|
|
22
22
|
@register_stack("ecr_library_module")
|
|
23
23
|
@register_stack("ecr_stack")
|
|
24
|
-
class ECRStack(IStack,
|
|
24
|
+
class ECRStack(IStack, StandardizedSsmMixin):
|
|
25
25
|
"""
|
|
26
26
|
A CloudFormation Stack for an ECR
|
|
27
27
|
|