BuzzerboyAWSLightsail 0.331.1__py3-none-any.whl → 0.333.1__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.
@@ -0,0 +1,154 @@
1
+ """
2
+ AWS Lightsail Base Standalone Infrastructure Stack
3
+ ==================================================
4
+
5
+ This module provides a standalone base class for AWS Lightsail infrastructure
6
+ stacks using CDKTF. It avoids AWSArchitectureBase and its compliance bootstrap
7
+ resources, while keeping IAM/Secrets/post-apply helpers used by Lightsail stacks.
8
+ """
9
+
10
+ import os
11
+ from cdktf import TerraformStack
12
+
13
+ from cdktf_cdktf_provider_aws.provider import AwsProvider
14
+ from cdktf_cdktf_provider_aws import (
15
+ iam_group,
16
+ iam_user,
17
+ iam_access_key,
18
+ iam_user_group_membership,
19
+ iam_group_policy,
20
+ )
21
+
22
+ from cdktf_cdktf_provider_random.provider import RandomProvider
23
+
24
+ from cdktf_cdktf_provider_null.provider import NullProvider
25
+
26
+ from .LightsailFlags import BaseLightsailArchitectureFlags
27
+ from .LightsailMixins import LightsailBaseMixin
28
+
29
+
30
+ class LightsailBaseStandalone(LightsailBaseMixin, TerraformStack):
31
+ """
32
+ Standalone base class for Lightsail stacks without AWSArchitectureBase.
33
+ """
34
+
35
+ resources = {}
36
+ default_post_apply_scripts = []
37
+
38
+ @staticmethod
39
+ def get_architecture_flags():
40
+ return BaseLightsailArchitectureFlags
41
+
42
+ def __init__(self, scope, id, **kwargs):
43
+ self.region = kwargs.get("region", "us-east-1")
44
+ self.environment = kwargs.get("environment", "dev")
45
+ self.project_name = kwargs.get("project_name")
46
+ self.profile = kwargs.get("profile", "default")
47
+
48
+ if not self.project_name:
49
+ raise ValueError("project_name is required and cannot be empty")
50
+
51
+ super().__init__(scope, id)
52
+
53
+ self.flags = kwargs.get("flags", [])
54
+ self.post_apply_scripts = kwargs.get("postApplyScripts", []) or []
55
+
56
+ default_secret_name = f"{self.project_name}/{self.environment}/credentials"
57
+ self.secret_name = kwargs.get("secret_name", default_secret_name)
58
+ self.default_signature_version = kwargs.get("default_signature_version", "s3v4")
59
+ self.default_extra_secret_env = kwargs.get("default_extra_secret_env", "SECRET_STRING")
60
+
61
+ default_bucket_name = self.properize_s3_bucketname(f"{self.region}-{self.project_name}-tfstate")
62
+ self.state_bucket_name = kwargs.get("state_bucket_name", default_bucket_name)
63
+
64
+ self.secrets = {}
65
+ self.post_terraform_messages = []
66
+ self._post_plan_guidance: list[str] = []
67
+
68
+ self._initialize_providers()
69
+ self._set_default_post_apply_scripts()
70
+ self._create_infrastructure_components()
71
+
72
+ def _initialize_providers(self):
73
+ aws = AwsProvider(self, "aws", region=self.region, profile=self.profile)
74
+ self.resources["aws"] = aws
75
+
76
+ RandomProvider(self, "random")
77
+ self.resources["random"] = RandomProvider
78
+
79
+ NullProvider(self, "null")
80
+ self.resources["null"] = NullProvider
81
+
82
+ def _set_default_post_apply_scripts(self):
83
+ self.default_post_apply_scripts = [
84
+ "echo '============================================='",
85
+ "echo '✅ Deployment Completed Successfully'",
86
+ "echo '============================================='",
87
+ f"echo '🏗️ Project: {self.project_name}'",
88
+ f"echo '🌍 Environment: {self.environment}'",
89
+ f"echo '📍 Region: {self.region}'",
90
+ "echo '============================================='",
91
+ "echo '💻 System Information:'",
92
+ "echo ' - OS: '$(uname -s)",
93
+ "echo ' - Architecture: '$(uname -m)",
94
+ "echo ' - User: '$(whoami)",
95
+ "echo ' - Working Directory: '$(pwd)",
96
+ "echo '============================================='",
97
+ "echo '✅ Post-deployment scripts execution started'",
98
+ ]
99
+
100
+ if BaseLightsailArchitectureFlags.SKIP_DEFAULT_POST_APPLY_SCRIPTS.value in self.flags:
101
+ return
102
+
103
+ self.post_apply_scripts = self.default_post_apply_scripts + self.post_apply_scripts
104
+
105
+ def _create_infrastructure_components(self):
106
+ self.create_iam_resources()
107
+ self.create_lightsail_resources()
108
+ self.create_security_resources()
109
+ self.execute_post_apply_scripts()
110
+ self.create_outputs()
111
+
112
+ def create_lightsail_resources(self):
113
+ raise NotImplementedError("create_lightsail_resources must be implemented by subclasses")
114
+
115
+ def create_outputs(self):
116
+ raise NotImplementedError("create_outputs must be implemented by subclasses")
117
+
118
+ def create_iam_resources(self):
119
+ user_name = f"{self.project_name}-service-user"
120
+ group_name = f"{self.project_name}-group"
121
+
122
+ self.service_group = iam_group.IamGroup(self, "service_group", name=group_name)
123
+ self.service_user = iam_user.IamUser(self, "service_user", name=user_name)
124
+
125
+ iam_user_group_membership.IamUserGroupMembership(
126
+ self,
127
+ "service_user_group_membership",
128
+ user=self.service_user.name,
129
+ groups=[self.service_group.name],
130
+ )
131
+
132
+ self.service_key = iam_access_key.IamAccessKey(
133
+ self, "service_key", user=self.service_user.name
134
+ )
135
+
136
+ try:
137
+ self.service_policy = self.create_iam_policy_from_file()
138
+ self.resources["iam_policy"] = self.service_policy
139
+ except FileNotFoundError:
140
+ pass
141
+
142
+ def create_iam_policy_from_file(self, file_path="iam_policy.json"):
143
+ file_to_open = os.path.join(os.path.dirname(__file__), file_path)
144
+
145
+ with open(file_to_open, "r") as f:
146
+ policy = f.read()
147
+
148
+ return iam_group_policy.IamGroupPolicy(
149
+ self,
150
+ f"{self.project_name}-{self.environment}-service-policy",
151
+ name=f"{self.project_name}-{self.environment}-service-policy",
152
+ group=self.service_group.name,
153
+ policy=policy,
154
+ )
@@ -22,30 +22,22 @@ The stack includes:
22
22
 
23
23
  #region specific imports
24
24
 
25
- import os
26
- import json
27
- from enum import Enum
28
25
  from constructs import Construct
29
- from cdktf import TerraformOutput
30
26
 
31
27
  # Import the base class
32
- from .LightsailBase import LightsailBase, BaseLightsailArchitectureFlags
28
+ from .LightsailBase import LightsailBase
29
+ from .LightsailFlags import ContainerArchitectureFlags
30
+ from .LightsailMixins import LightsailContainerMixin
33
31
 
34
32
  #endregion
35
33
 
36
34
  #region AWS Provider and Resources
37
35
  from cdktf_cdktf_provider_aws.provider import AwsProvider
38
36
  from cdktf_cdktf_provider_aws import (
39
- lightsail_container_service,
40
- lightsail_database,
41
37
  cloudfront_distribution,
42
- s3_bucket,
43
38
  )
44
39
  #endregion
45
40
 
46
- #region Random Provider and Resources
47
- from cdktf_cdktf_provider_random import password
48
-
49
41
  # AWS WAF (currently unused but imported for future use)
50
42
  from cdktf_cdktf_provider_aws.wafv2_web_acl import (
51
43
  Wafv2WebAcl,
@@ -65,36 +57,10 @@ from cdktf_cdktf_provider_aws.wafv2_rule_group import Wafv2RuleGroupRuleVisibili
65
57
 
66
58
 
67
59
 
68
- #region ArchitectureFlags
69
- class ArchitectureFlags(Enum):
70
- """
71
- Architecture configuration flags for optional components.
72
-
73
- Includes both base flags and container-specific flags.
74
-
75
- Base flags:
76
- :param SKIP_DEFAULT_POST_APPLY_SCRIPTS: Skip default post-apply scripts
77
- :param PRESERVE_EXISTING_SECRETS: Don't overwrite existing secret versions (smart detection)
78
- :param IGNORE_SECRET_CHANGES: Ignore all changes to secret after initial creation
79
-
80
- Container-specific flags:
81
- :param SKIP_DATABASE: Skip database creation
82
- :param SKIP_DOMAIN: Skip domain and DNS configuration
83
- """
84
-
85
- # Base flags from BaseLightsailArchitectureFlags
86
- SKIP_DEFAULT_POST_APPLY_SCRIPTS = "skip_default_post_apply_scripts"
87
- PRESERVE_EXISTING_SECRETS = "preserve_existing_secrets"
88
- IGNORE_SECRET_CHANGES = "ignore_secret_changes"
89
-
90
- # Container-specific flags
91
- SKIP_DATABASE = "skip_database"
92
- SKIP_DOMAIN = "skip_domain"
93
-
94
- #endregion
60
+ ArchitectureFlags = ContainerArchitectureFlags
95
61
 
96
62
 
97
- class LightsailContainerStack(LightsailBase):
63
+ class LightsailContainerStack(LightsailContainerMixin, LightsailBase):
98
64
  """
99
65
  AWS Lightsail Mini Infrastructure Stack.
100
66
 
@@ -128,7 +94,7 @@ class LightsailContainerStack(LightsailBase):
128
94
  :returns: ArchitectureFlags enum class
129
95
  :rtype: type[ArchitectureFlags]
130
96
  """
131
- return ArchitectureFlags
97
+ return ContainerArchitectureFlags
132
98
 
133
99
  def __init__(self, scope, id, **kwargs):
134
100
  """
@@ -177,205 +143,3 @@ class LightsailContainerStack(LightsailBase):
177
143
  self, "aws_domain", region="us-east-1", profile=self.profile, alias="domain"
178
144
  )
179
145
  self.resources["aws_domain"] = self.aws_domain_provider
180
-
181
- def _set_default_post_apply_scripts(self):
182
- """
183
- Set default post-apply scripts specific to container deployments.
184
- """
185
- # Call parent method for base scripts
186
- super()._set_default_post_apply_scripts()
187
-
188
- # Skip if flag is set
189
- if BaseLightsailArchitectureFlags.SKIP_DEFAULT_POST_APPLY_SCRIPTS.value in self.flags:
190
- return
191
-
192
- # Add container-specific scripts
193
- container_scripts = [
194
- f"echo '🚀 Container Service URL: https://{self.project_name}.{self.region}.cs.amazonlightsail.com'",
195
- ]
196
-
197
- # Insert container-specific scripts before the final "execution started" message
198
- if self.post_apply_scripts:
199
- # Find the index of the last script and insert before it
200
- insert_index = len(self.post_apply_scripts) - 1
201
- for script in reversed(container_scripts):
202
- self.post_apply_scripts.insert(insert_index, script)
203
-
204
- def create_lightsail_resources(self):
205
- """
206
- Create core Lightsail resources.
207
-
208
- Creates:
209
- * Lightsail Container Service with nano power and scale of 1
210
- * Random password for database authentication (if database not skipped)
211
-
212
- .. note::
213
- Custom domains are configured separately through DNS records and
214
- post-deployment automation rather than the public_domain_names parameter
215
- due to CDKTF type complexity.
216
- """
217
- # Lightsail Container Service
218
- self.container_service = lightsail_container_service.LightsailContainerService(
219
- self,
220
- "app_container",
221
- name=f"{self.project_name}",
222
- power="nano",
223
- region=self.region,
224
- scale=1,
225
- is_disabled=False,
226
- # Note: Custom domains are configured separately via DNS records
227
- # The public_domain_names parameter has complex type requirements
228
- tags={"Environment": self.environment, "Project": self.project_name, "Stack": self.__class__.__name__},
229
- )
230
- self.container_service_url = self.get_lightsail_container_service_domain()
231
-
232
- # Create Lightsail database if not skipped
233
- if not self.has_flag(ArchitectureFlags.SKIP_DATABASE.value):
234
- self.create_lightsail_database()
235
-
236
- # Create S3 bucket for application data
237
- self.create_s3_bucket()
238
-
239
- self.resources["lightsail_container_service"] = self.container_service
240
-
241
- def create_lightsail_database(self):
242
- """
243
- Create Lightsail PostgreSQL database (optional).
244
-
245
- Creates a micro PostgreSQL 14 database instance if the SKIP_DATABASE flag
246
- is not set. Also populates the secrets dictionary with database connection
247
- information for use in Secrets Manager.
248
-
249
- Database Configuration:
250
- * Engine: PostgreSQL 14
251
- * Size: micro_2_0
252
- * Final snapshot: Disabled (skip_final_snapshot=True)
253
-
254
- .. note::
255
- Database creation can be skipped by including ArchitectureFlags.SKIP_DATABASE
256
- in the flags parameter during stack initialization.
257
- """
258
- # Database Password Generation
259
- self.db_password = password.Password(
260
- self, "db_password", length=16, special=True, override_special="!#$%&*()-_=+[]{}<>:?"
261
- )
262
-
263
- self.database = lightsail_database.LightsailDatabase(
264
- self,
265
- "app_database",
266
- relational_database_name=f"{self.project_name}-db",
267
- blueprint_id="postgres_14",
268
- bundle_id="micro_2_0",
269
- master_database_name=self.clean_hyphens(f"{self.project_name}"),
270
- master_username=self.default_db_username,
271
- master_password=self.db_password.result,
272
- skip_final_snapshot=True,
273
- tags={"Environment": self.environment, "Project": self.project_name, "Stack": self.__class__.__name__},
274
- )
275
-
276
- # Populate secrets for database connection
277
- self.secrets.update(
278
- {
279
- "password": self.db_password.result,
280
- "username": self.default_db_username,
281
- "dbname": self.default_db_name,
282
- "host": self.database.master_endpoint_address,
283
- "port": self.database.master_endpoint_port,
284
- }
285
- )
286
-
287
- def create_s3_bucket(self, bucket_name=None):
288
- """
289
- Create S3 bucket for application data storage.
290
-
291
- Creates a private S3 bucket with proper tagging for application data storage
292
- and security configurations:
293
- - Bucket versioning enabled
294
- - Server-side encryption with Amazon S3 managed keys (SSE-S3)
295
- - Bucket key enabled to reduce encryption costs
296
- - Private ACL
297
-
298
- The bucket name follows the pattern: {project_name}-s3
299
-
300
- .. note::
301
- The ACL parameter is deprecated in favor of aws_s3_bucket_acl resource
302
- but is retained for backwards compatibility.
303
- """
304
- if bucket_name is None:
305
- bucket_name = self.properize_s3_bucketname(f"{self.project_name}-s3")
306
-
307
- self.s3_bucket = s3_bucket.S3Bucket(
308
- self,
309
- "app_data_bucket",
310
- bucket=bucket_name,
311
- acl="private",
312
- versioning={"enabled": True},
313
- server_side_encryption_configuration=({
314
- "rule": ({
315
- "apply_server_side_encryption_by_default": {
316
- "sse_algorithm": "AES256"
317
- },
318
- "bucket_key_enabled": True
319
- })
320
- }),
321
- tags={"Environment": self.environment, "Project": self.project_name, "Stack": self.__class__.__name__},
322
- )
323
-
324
- # Store the S3 bucket in resources registry
325
- self.resources["s3_bucket"] = self.s3_bucket
326
- self.bucket_name = bucket_name
327
-
328
- def get_lightsail_container_service_domain(self):
329
- """
330
- Retrieve the actual Lightsail container service domain from AWS.
331
-
332
- Returns a default format domain since we cannot query AWS at synthesis time.
333
-
334
- :returns: The public domain URL for the container service
335
- :rtype: str
336
- """
337
- return f"{self.project_name}.{self.region}.cs.amazonlightsail.com"
338
-
339
- def create_outputs(self):
340
- """
341
- Create Terraform outputs for important resource information.
342
-
343
- Generates outputs for:
344
- * Container service public URL
345
- * Database endpoint (if database is enabled)
346
- * Database password (sensitive, if database is enabled)
347
- * IAM access keys (sensitive)
348
-
349
- .. note::
350
- Sensitive outputs are marked as such and will be hidden in
351
- Terraform output unless explicitly requested.
352
- """
353
- # Container service public URL
354
- TerraformOutput(
355
- self,
356
- "container_service_url",
357
- value=self.container_service_url,
358
- description="Public URL of the Lightsail container service",
359
- )
360
-
361
- # Database outputs (if database is enabled)
362
- if not self.has_flag(ArchitectureFlags.SKIP_DATABASE.value) and hasattr(self, 'database'):
363
- TerraformOutput(
364
- self,
365
- "database_endpoint",
366
- value=f"{self.database.master_endpoint_address}:{self.database.master_endpoint_port}",
367
- description="Database connection endpoint",
368
- )
369
- TerraformOutput(
370
- self,
371
- "database_password",
372
- value=self.database.master_password,
373
- sensitive=True,
374
- description="Database master password (sensitive)",
375
- )
376
-
377
- # Use the shared IAM output helper
378
- self.create_iam_outputs()
379
-
380
- """Placeholder for networking resources creation."""
381
- pass
@@ -0,0 +1,149 @@
1
+ """
2
+ AWS Lightsail Mini Infrastructure Stack
3
+ ======================================
4
+
5
+ This module provides a comprehensive AWS Lightsail infrastructure deployment stack
6
+ using CDKTF (Cloud Development Kit for Terraform) with Python.
7
+
8
+ The stack includes:
9
+ * Lightsail Container Service with automatic custom domain attachment
10
+ * PostgreSQL Database (optional)
11
+ * DNS management with CNAME records
12
+ * SSL certificate management with automatic validation
13
+ * IAM resources for service access
14
+ * S3 bucket for application data
15
+ * Secrets Manager for credential storage
16
+
17
+ :author: Generated with GitHub Copilot
18
+ :version: 1.0.0
19
+ :license: MIT
20
+ """
21
+
22
+
23
+ #region specific imports
24
+
25
+ from constructs import Construct
26
+
27
+ # Import the base class
28
+ from .LightsailBaseStandalone import LightsailBaseStandalone
29
+ from .LightsailFlags import ContainerArchitectureFlags
30
+ from .LightsailMixins import LightsailContainerMixin
31
+
32
+ #endregion
33
+
34
+ #region AWS Provider and Resources
35
+ from cdktf_cdktf_provider_aws.provider import AwsProvider
36
+ from cdktf_cdktf_provider_aws import (
37
+ cloudfront_distribution,
38
+ )
39
+ #endregion
40
+
41
+ # AWS WAF (currently unused but imported for future use)
42
+ from cdktf_cdktf_provider_aws.wafv2_web_acl import (
43
+ Wafv2WebAcl,
44
+ Wafv2WebAclDefaultAction,
45
+ Wafv2WebAclRule,
46
+ Wafv2WebAclVisibilityConfig,
47
+ Wafv2WebAclDefaultActionAllow,
48
+ Wafv2WebAclRuleOverrideAction,
49
+ Wafv2WebAclRuleOverrideActionNone,
50
+ Wafv2WebAclRuleOverrideActionCount,
51
+ Wafv2WebAclRuleVisibilityConfig,
52
+ )
53
+ from cdktf_cdktf_provider_aws.wafv2_web_acl_association import Wafv2WebAclAssociation
54
+ from cdktf_cdktf_provider_aws.wafv2_rule_group import Wafv2RuleGroupRuleVisibilityConfig
55
+
56
+ #endregion
57
+
58
+
59
+
60
+ ArchitectureFlags = ContainerArchitectureFlags
61
+
62
+
63
+ class LightsailContainerStandaloneStack(LightsailContainerMixin, LightsailBaseStandalone):
64
+ """
65
+ AWS Lightsail Mini Infrastructure Stack.
66
+
67
+ A comprehensive infrastructure stack that deploys:
68
+ * Lightsail Container Service with custom domain support
69
+ * PostgreSQL database (optional)
70
+ * IAM resources and S3 storage
71
+
72
+ :param scope: The construct scope
73
+ :param id: The construct ID
74
+ :param kwargs: Configuration parameters including region, domains, flags, etc.
75
+
76
+ Example:
77
+ >>> stack = LightsailContainerStandaloneStack(
78
+ ... app, "my-stack",
79
+ ... region="ca-central-1",
80
+ ... domains=["app.example.com"],
81
+ ... project_name="my-app",
82
+ ... postApplyScripts=[
83
+ ... "echo 'Deployment completed'",
84
+ ... "curl -X POST https://webhook.example.com/notify"
85
+ ... ]
86
+ ... )
87
+ """
88
+
89
+ @staticmethod
90
+ def get_architecture_flags():
91
+ """
92
+ Get the ArchitectureFlags enum for configuration.
93
+
94
+ :returns: ArchitectureFlags enum class
95
+ :rtype: type[ArchitectureFlags]
96
+ """
97
+ return ContainerArchitectureFlags
98
+
99
+ def __init__(self, scope, id, **kwargs):
100
+ """
101
+ Initialize the AWS Lightsail Mini Infrastructure Stack.
102
+
103
+ :param scope: The construct scope
104
+ :param id: Unique identifier for this stack
105
+ :param kwargs: Configuration parameters
106
+
107
+ **Configuration Parameters:**
108
+
109
+ :param region: AWS region (default: "us-east-1")
110
+ :param environment: Environment name (default: "dev")
111
+ :param project_name: Project identifier (default: "bb-aws-lightsail-mini-v1a-app")
112
+ :param domain_name: Primary domain name
113
+ :param domains: List of custom domains to configure
114
+ :param flags: List of ArchitectureFlags to modify behavior
115
+ :param profile: AWS profile to use (default: "default")
116
+ :param postApplyScripts: List of shell commands to execute after deployment
117
+
118
+ .. warning::
119
+ Lightsail domain operations must use us-east-1 region regardless of
120
+ the main stack region.
121
+ """
122
+ # Set container-specific defaults
123
+ if "project_name" not in kwargs:
124
+ kwargs["project_name"] = "bb-aws-lightsail-mini-v1a-app"
125
+
126
+ # Set database defaults before base initialization
127
+ self.default_db_name = kwargs.get("default_db_name", kwargs["project_name"])
128
+ self.default_db_username = kwargs.get("default_db_username", "dbadmin")
129
+
130
+ # Call parent constructor which handles all the base initialization
131
+ super().__init__(scope, id, **kwargs)
132
+
133
+ # ===== Container-Specific Configuration =====
134
+ self.domains = kwargs.get("domains", []) or []
135
+
136
+ # ===== Database Configuration =====
137
+ self.default_db_name = kwargs.get("default_db_name", self.project_name)
138
+ self.default_db_username = kwargs.get("default_db_username", "dbadmin")
139
+
140
+ def _initialize_providers(self):
141
+ """Initialize all required Terraform providers."""
142
+ # Call parent class to initialize base providers
143
+ super()._initialize_providers()
144
+
145
+ # Add Lightsail-specific provider for domain operations (must be us-east-1)
146
+ self.aws_domain_provider = AwsProvider(
147
+ self, "aws_domain", region="us-east-1", profile=self.profile, alias="domain"
148
+ )
149
+ self.resources["aws_domain"] = self.aws_domain_provider