BuzzerboyAWSLightsail 0.331.1__py3-none-any.whl → 0.332.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.
- BuzzerboyAWSLightsailStack/ArchitectureMaker.py +229 -0
- BuzzerboyAWSLightsailStack/LightsailBaseStandalone.py +318 -0
- BuzzerboyAWSLightsailStack/LightsailContainerStandalone.py +385 -0
- BuzzerboyAWSLightsailStack/LightsailDatabaseStandalone.py +484 -0
- BuzzerboyAWSLightsailStack/__init__.py +1 -0
- {buzzerboyawslightsail-0.331.1.dist-info → buzzerboyawslightsail-0.332.1.dist-info}/METADATA +91 -1
- buzzerboyawslightsail-0.332.1.dist-info/RECORD +15 -0
- {buzzerboyawslightsail-0.331.1.dist-info → buzzerboyawslightsail-0.332.1.dist-info}/licenses/LICENSE +0 -0
- buzzerboyawslightsail-0.331.1.dist-info/RECORD +0 -11
- {buzzerboyawslightsail-0.331.1.dist-info → buzzerboyawslightsail-0.332.1.dist-info}/WHEEL +0 -0
- {buzzerboyawslightsail-0.331.1.dist-info → buzzerboyawslightsail-0.332.1.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
"""
|
|
2
|
+
ArchitectureMaker
|
|
3
|
+
=================
|
|
4
|
+
|
|
5
|
+
Helper routines to build common Lightsail architectures using CDKTF.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from typing import Any, Dict, List
|
|
9
|
+
|
|
10
|
+
from cdktf import App
|
|
11
|
+
|
|
12
|
+
from BuzzerboyAWSLightsailStack.LightsailDatabase import (
|
|
13
|
+
LightsailDatabaseStack,
|
|
14
|
+
)
|
|
15
|
+
from BuzzerboyAWSLightsailStack.LightsailDatabaseStandalone import (
|
|
16
|
+
LightsailDatabaseStandaloneStack,
|
|
17
|
+
)
|
|
18
|
+
from BuzzerboyAWSLightsailStack.LightsailContainer import (
|
|
19
|
+
LightsailContainerStack,
|
|
20
|
+
)
|
|
21
|
+
from BuzzerboyAWSLightsailStack.LightsailContainerStandalone import (
|
|
22
|
+
LightsailContainerStandaloneStack,
|
|
23
|
+
)
|
|
24
|
+
from BuzzerboyArchetypeStack.BuzzerboyArchetype import BuzzerboyArchetype
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class ArchitectureMaker:
|
|
28
|
+
"""
|
|
29
|
+
Factory utilities for building Lightsail architectures from a definition dict.
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
@staticmethod
|
|
33
|
+
def auto_stack_db_only(definition: Dict[str, Any], include_compliance: bool = False) -> App:
|
|
34
|
+
"""
|
|
35
|
+
Create a DB-only Lightsail stack from a definition dictionary.
|
|
36
|
+
|
|
37
|
+
Expected keys in definition:
|
|
38
|
+
- product (str)
|
|
39
|
+
- name or app (str)
|
|
40
|
+
- tier (str)
|
|
41
|
+
- organization (str)
|
|
42
|
+
- region (str)
|
|
43
|
+
- databases (list[str])
|
|
44
|
+
Optional keys:
|
|
45
|
+
- profile (str)
|
|
46
|
+
- db_instance_size (str)
|
|
47
|
+
- master_username (str)
|
|
48
|
+
- flags (list[str])
|
|
49
|
+
"""
|
|
50
|
+
if not isinstance(definition, dict):
|
|
51
|
+
raise ValueError("definition must be a dictionary")
|
|
52
|
+
|
|
53
|
+
product = definition.get("product")
|
|
54
|
+
app_name = definition.get("name") or definition.get("app")
|
|
55
|
+
tier = definition.get("tier")
|
|
56
|
+
organization = definition.get("organization")
|
|
57
|
+
region = definition.get("region")
|
|
58
|
+
databases = definition.get("databases", [])
|
|
59
|
+
|
|
60
|
+
missing = [key for key, value in {
|
|
61
|
+
"product": product,
|
|
62
|
+
"name/app": app_name,
|
|
63
|
+
"tier": tier,
|
|
64
|
+
"organization": organization,
|
|
65
|
+
"region": region,
|
|
66
|
+
"databases": databases,
|
|
67
|
+
}.items() if not value]
|
|
68
|
+
if missing:
|
|
69
|
+
raise ValueError(f"definition is missing required keys: {', '.join(missing)}")
|
|
70
|
+
|
|
71
|
+
archetype = BuzzerboyArchetype(
|
|
72
|
+
product=product,
|
|
73
|
+
app=app_name,
|
|
74
|
+
tier=tier,
|
|
75
|
+
organization=organization,
|
|
76
|
+
region=region,
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
flags: List[str] = list(definition.get("flags", []))
|
|
80
|
+
flags = list(dict.fromkeys(flags))
|
|
81
|
+
|
|
82
|
+
app = App()
|
|
83
|
+
|
|
84
|
+
stack_class = LightsailDatabaseStack if include_compliance else LightsailDatabaseStandaloneStack
|
|
85
|
+
stack_class(
|
|
86
|
+
app,
|
|
87
|
+
f"{archetype.get_project_name()}-db-stack",
|
|
88
|
+
project_name=archetype.get_project_name(),
|
|
89
|
+
environment=archetype.get_tier(),
|
|
90
|
+
region=archetype.get_region(),
|
|
91
|
+
secret_name=archetype.get_secret_name(),
|
|
92
|
+
databases=databases,
|
|
93
|
+
profile=definition.get("profile", "default"),
|
|
94
|
+
db_instance_size=definition.get("db_instance_size", "micro_2_0"),
|
|
95
|
+
master_username=definition.get("master_username", "dbmasteruser"),
|
|
96
|
+
flags=flags,
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
app.synth()
|
|
100
|
+
return app
|
|
101
|
+
|
|
102
|
+
@staticmethod
|
|
103
|
+
def auto_main_container_only(definition: Dict[str, Any], include_compliance: bool = False) -> App:
|
|
104
|
+
"""
|
|
105
|
+
Create a container-only Lightsail stack from a definition dictionary.
|
|
106
|
+
|
|
107
|
+
Expected keys in definition:
|
|
108
|
+
- product (str)
|
|
109
|
+
- name or app (str)
|
|
110
|
+
- tier (str)
|
|
111
|
+
- organization (str)
|
|
112
|
+
- region (str)
|
|
113
|
+
Optional keys:
|
|
114
|
+
- profile (str)
|
|
115
|
+
- flags (list[str])
|
|
116
|
+
"""
|
|
117
|
+
if not isinstance(definition, dict):
|
|
118
|
+
raise ValueError("definition must be a dictionary")
|
|
119
|
+
|
|
120
|
+
product = definition.get("product")
|
|
121
|
+
app_name = definition.get("name") or definition.get("app")
|
|
122
|
+
tier = definition.get("tier")
|
|
123
|
+
organization = definition.get("organization")
|
|
124
|
+
region = definition.get("region")
|
|
125
|
+
|
|
126
|
+
missing = [key for key, value in {
|
|
127
|
+
"product": product,
|
|
128
|
+
"name/app": app_name,
|
|
129
|
+
"tier": tier,
|
|
130
|
+
"organization": organization,
|
|
131
|
+
"region": region,
|
|
132
|
+
}.items() if not value]
|
|
133
|
+
if missing:
|
|
134
|
+
raise ValueError(f"definition is missing required keys: {', '.join(missing)}")
|
|
135
|
+
|
|
136
|
+
archetype = BuzzerboyArchetype(
|
|
137
|
+
product=product,
|
|
138
|
+
app=app_name,
|
|
139
|
+
tier=tier,
|
|
140
|
+
organization=organization,
|
|
141
|
+
region=region,
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
flags: List[str] = list(definition.get("flags", []))
|
|
145
|
+
flags.extend([
|
|
146
|
+
"skip_database",
|
|
147
|
+
"skip_domain",
|
|
148
|
+
])
|
|
149
|
+
flags = list(dict.fromkeys(flags))
|
|
150
|
+
|
|
151
|
+
app = App()
|
|
152
|
+
|
|
153
|
+
stack_class = LightsailContainerStack if include_compliance else LightsailContainerStandaloneStack
|
|
154
|
+
stack_class(
|
|
155
|
+
app,
|
|
156
|
+
f"{archetype.get_project_name()}-stack",
|
|
157
|
+
project_name=archetype.get_project_name(),
|
|
158
|
+
environment=archetype.get_tier(),
|
|
159
|
+
region=archetype.get_region(),
|
|
160
|
+
secret_name=archetype.get_secret_name(),
|
|
161
|
+
profile=definition.get("profile", "default"),
|
|
162
|
+
flags=flags,
|
|
163
|
+
)
|
|
164
|
+
|
|
165
|
+
app.synth()
|
|
166
|
+
return app
|
|
167
|
+
|
|
168
|
+
@staticmethod
|
|
169
|
+
def auto_main(definition: Dict[str, Any], include_compliance: bool = False) -> App:
|
|
170
|
+
"""
|
|
171
|
+
Create a container + database Lightsail stack from a definition dictionary.
|
|
172
|
+
|
|
173
|
+
Expected keys in definition:
|
|
174
|
+
- product (str)
|
|
175
|
+
- name or app (str)
|
|
176
|
+
- tier (str)
|
|
177
|
+
- organization (str)
|
|
178
|
+
- region (str)
|
|
179
|
+
Optional keys:
|
|
180
|
+
- profile (str)
|
|
181
|
+
- flags (list[str])
|
|
182
|
+
"""
|
|
183
|
+
if not isinstance(definition, dict):
|
|
184
|
+
raise ValueError("definition must be a dictionary")
|
|
185
|
+
|
|
186
|
+
product = definition.get("product")
|
|
187
|
+
app_name = definition.get("name") or definition.get("app")
|
|
188
|
+
tier = definition.get("tier")
|
|
189
|
+
organization = definition.get("organization")
|
|
190
|
+
region = definition.get("region")
|
|
191
|
+
|
|
192
|
+
missing = [key for key, value in {
|
|
193
|
+
"product": product,
|
|
194
|
+
"name/app": app_name,
|
|
195
|
+
"tier": tier,
|
|
196
|
+
"organization": organization,
|
|
197
|
+
"region": region,
|
|
198
|
+
}.items() if not value]
|
|
199
|
+
if missing:
|
|
200
|
+
raise ValueError(f"definition is missing required keys: {', '.join(missing)}")
|
|
201
|
+
|
|
202
|
+
archetype = BuzzerboyArchetype(
|
|
203
|
+
product=product,
|
|
204
|
+
app=app_name,
|
|
205
|
+
tier=tier,
|
|
206
|
+
organization=organization,
|
|
207
|
+
region=region,
|
|
208
|
+
)
|
|
209
|
+
|
|
210
|
+
flags: List[str] = list(definition.get("flags", []))
|
|
211
|
+
flags.append("skip_domain")
|
|
212
|
+
flags = list(dict.fromkeys(flags))
|
|
213
|
+
|
|
214
|
+
app = App()
|
|
215
|
+
|
|
216
|
+
stack_class = LightsailContainerStack if include_compliance else LightsailContainerStandaloneStack
|
|
217
|
+
stack_class(
|
|
218
|
+
app,
|
|
219
|
+
f"{archetype.get_project_name()}-stack",
|
|
220
|
+
project_name=archetype.get_project_name(),
|
|
221
|
+
environment=archetype.get_tier(),
|
|
222
|
+
region=archetype.get_region(),
|
|
223
|
+
secret_name=archetype.get_secret_name(),
|
|
224
|
+
profile=definition.get("profile", "default"),
|
|
225
|
+
flags=flags,
|
|
226
|
+
)
|
|
227
|
+
|
|
228
|
+
app.synth()
|
|
229
|
+
return app
|
|
@@ -0,0 +1,318 @@
|
|
|
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
|
+
import json
|
|
12
|
+
from enum import Enum
|
|
13
|
+
from cdktf import TerraformStack, TerraformOutput
|
|
14
|
+
|
|
15
|
+
from cdktf_cdktf_provider_aws.provider import AwsProvider
|
|
16
|
+
from cdktf_cdktf_provider_aws import (
|
|
17
|
+
iam_group,
|
|
18
|
+
iam_user,
|
|
19
|
+
iam_access_key,
|
|
20
|
+
iam_user_group_membership,
|
|
21
|
+
iam_group_policy,
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
from cdktf_cdktf_provider_random import password
|
|
25
|
+
from cdktf_cdktf_provider_random.provider import RandomProvider
|
|
26
|
+
|
|
27
|
+
from cdktf_cdktf_provider_null.provider import NullProvider
|
|
28
|
+
from cdktf_cdktf_provider_null.resource import Resource as NullResource
|
|
29
|
+
|
|
30
|
+
from cdktf_cdktf_provider_aws.secretsmanager_secret import SecretsmanagerSecret
|
|
31
|
+
from cdktf_cdktf_provider_aws.secretsmanager_secret_version import SecretsmanagerSecretVersion
|
|
32
|
+
from cdktf_cdktf_provider_aws.data_aws_secretsmanager_secret_version import DataAwsSecretsmanagerSecretVersion
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class BaseLightsailArchitectureFlags(Enum):
|
|
36
|
+
"""
|
|
37
|
+
Base architecture configuration flags for optional components.
|
|
38
|
+
|
|
39
|
+
:param SKIP_DEFAULT_POST_APPLY_SCRIPTS: Skip default post-apply scripts
|
|
40
|
+
:param PRESERVE_EXISTING_SECRETS: Don't overwrite existing secret versions
|
|
41
|
+
:param IGNORE_SECRET_CHANGES: Ignore all changes to secret after initial creation
|
|
42
|
+
"""
|
|
43
|
+
|
|
44
|
+
SKIP_DEFAULT_POST_APPLY_SCRIPTS = "skip_default_post_apply_scripts"
|
|
45
|
+
PRESERVE_EXISTING_SECRETS = "preserve_existing_secrets"
|
|
46
|
+
IGNORE_SECRET_CHANGES = "ignore_secret_changes"
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class LightsailBaseStandalone(TerraformStack):
|
|
50
|
+
"""
|
|
51
|
+
Standalone base class for Lightsail stacks without AWSArchitectureBase.
|
|
52
|
+
"""
|
|
53
|
+
|
|
54
|
+
resources = {}
|
|
55
|
+
default_post_apply_scripts = []
|
|
56
|
+
|
|
57
|
+
@staticmethod
|
|
58
|
+
def get_architecture_flags():
|
|
59
|
+
return BaseLightsailArchitectureFlags
|
|
60
|
+
|
|
61
|
+
def __init__(self, scope, id, **kwargs):
|
|
62
|
+
self.region = kwargs.get("region", "us-east-1")
|
|
63
|
+
self.environment = kwargs.get("environment", "dev")
|
|
64
|
+
self.project_name = kwargs.get("project_name")
|
|
65
|
+
self.profile = kwargs.get("profile", "default")
|
|
66
|
+
|
|
67
|
+
if not self.project_name:
|
|
68
|
+
raise ValueError("project_name is required and cannot be empty")
|
|
69
|
+
|
|
70
|
+
super().__init__(scope, id)
|
|
71
|
+
|
|
72
|
+
self.flags = kwargs.get("flags", [])
|
|
73
|
+
self.post_apply_scripts = kwargs.get("postApplyScripts", []) or []
|
|
74
|
+
|
|
75
|
+
default_secret_name = f"{self.project_name}/{self.environment}/credentials"
|
|
76
|
+
self.secret_name = kwargs.get("secret_name", default_secret_name)
|
|
77
|
+
self.default_signature_version = kwargs.get("default_signature_version", "s3v4")
|
|
78
|
+
self.default_extra_secret_env = kwargs.get("default_extra_secret_env", "SECRET_STRING")
|
|
79
|
+
|
|
80
|
+
default_bucket_name = self.properize_s3_bucketname(f"{self.region}-{self.project_name}-tfstate")
|
|
81
|
+
self.state_bucket_name = kwargs.get("state_bucket_name", default_bucket_name)
|
|
82
|
+
|
|
83
|
+
self.secrets = {}
|
|
84
|
+
self.post_terraform_messages = []
|
|
85
|
+
self._post_plan_guidance: list[str] = []
|
|
86
|
+
|
|
87
|
+
self._initialize_providers()
|
|
88
|
+
self._set_default_post_apply_scripts()
|
|
89
|
+
self._create_infrastructure_components()
|
|
90
|
+
|
|
91
|
+
def _initialize_providers(self):
|
|
92
|
+
aws = AwsProvider(self, "aws", region=self.region, profile=self.profile)
|
|
93
|
+
self.resources["aws"] = aws
|
|
94
|
+
|
|
95
|
+
RandomProvider(self, "random")
|
|
96
|
+
self.resources["random"] = RandomProvider
|
|
97
|
+
|
|
98
|
+
NullProvider(self, "null")
|
|
99
|
+
self.resources["null"] = NullProvider
|
|
100
|
+
|
|
101
|
+
def _set_default_post_apply_scripts(self):
|
|
102
|
+
self.default_post_apply_scripts = [
|
|
103
|
+
"echo '============================================='",
|
|
104
|
+
"echo '✅ Deployment Completed Successfully'",
|
|
105
|
+
"echo '============================================='",
|
|
106
|
+
f"echo '🏗️ Project: {self.project_name}'",
|
|
107
|
+
f"echo '🌍 Environment: {self.environment}'",
|
|
108
|
+
f"echo '📍 Region: {self.region}'",
|
|
109
|
+
"echo '============================================='",
|
|
110
|
+
"echo '💻 System Information:'",
|
|
111
|
+
"echo ' - OS: '$(uname -s)",
|
|
112
|
+
"echo ' - Architecture: '$(uname -m)",
|
|
113
|
+
"echo ' - User: '$(whoami)",
|
|
114
|
+
"echo ' - Working Directory: '$(pwd)",
|
|
115
|
+
"echo '============================================='",
|
|
116
|
+
"echo '✅ Post-deployment scripts execution started'",
|
|
117
|
+
]
|
|
118
|
+
|
|
119
|
+
if BaseLightsailArchitectureFlags.SKIP_DEFAULT_POST_APPLY_SCRIPTS.value in self.flags:
|
|
120
|
+
return
|
|
121
|
+
|
|
122
|
+
self.post_apply_scripts = self.default_post_apply_scripts + self.post_apply_scripts
|
|
123
|
+
|
|
124
|
+
def _create_infrastructure_components(self):
|
|
125
|
+
self.create_iam_resources()
|
|
126
|
+
self.create_lightsail_resources()
|
|
127
|
+
self.create_security_resources()
|
|
128
|
+
self.execute_post_apply_scripts()
|
|
129
|
+
self.create_outputs()
|
|
130
|
+
|
|
131
|
+
def create_lightsail_resources(self):
|
|
132
|
+
raise NotImplementedError("create_lightsail_resources must be implemented by subclasses")
|
|
133
|
+
|
|
134
|
+
def create_outputs(self):
|
|
135
|
+
raise NotImplementedError("create_outputs must be implemented by subclasses")
|
|
136
|
+
|
|
137
|
+
def create_iam_resources(self):
|
|
138
|
+
user_name = f"{self.project_name}-service-user"
|
|
139
|
+
group_name = f"{self.project_name}-group"
|
|
140
|
+
|
|
141
|
+
self.service_group = iam_group.IamGroup(self, "service_group", name=group_name)
|
|
142
|
+
self.service_user = iam_user.IamUser(self, "service_user", name=user_name)
|
|
143
|
+
|
|
144
|
+
iam_user_group_membership.IamUserGroupMembership(
|
|
145
|
+
self,
|
|
146
|
+
"service_user_group_membership",
|
|
147
|
+
user=self.service_user.name,
|
|
148
|
+
groups=[self.service_group.name],
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
self.service_key = iam_access_key.IamAccessKey(
|
|
152
|
+
self, "service_key", user=self.service_user.name
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
try:
|
|
156
|
+
self.service_policy = self.create_iam_policy_from_file()
|
|
157
|
+
self.resources["iam_policy"] = self.service_policy
|
|
158
|
+
except FileNotFoundError:
|
|
159
|
+
pass
|
|
160
|
+
|
|
161
|
+
def create_iam_policy_from_file(self, file_path="iam_policy.json"):
|
|
162
|
+
file_to_open = os.path.join(os.path.dirname(__file__), file_path)
|
|
163
|
+
|
|
164
|
+
with open(file_to_open, "r") as f:
|
|
165
|
+
policy = f.read()
|
|
166
|
+
|
|
167
|
+
return iam_group_policy.IamGroupPolicy(
|
|
168
|
+
self,
|
|
169
|
+
f"{self.project_name}-{self.environment}-service-policy",
|
|
170
|
+
name=f"{self.project_name}-{self.environment}-service-policy",
|
|
171
|
+
group=self.service_group.name,
|
|
172
|
+
policy=policy,
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
def get_extra_secret_env(self, env_var_name=None):
|
|
176
|
+
if env_var_name is None:
|
|
177
|
+
env_var_name = self.default_extra_secret_env
|
|
178
|
+
|
|
179
|
+
extra_secret_env = os.environ.get(env_var_name, None)
|
|
180
|
+
|
|
181
|
+
if extra_secret_env:
|
|
182
|
+
try:
|
|
183
|
+
extra_secret_json = json.loads(extra_secret_env)
|
|
184
|
+
for key, value in extra_secret_json.items():
|
|
185
|
+
if key not in self.secrets:
|
|
186
|
+
self.secrets[key] = value
|
|
187
|
+
except json.JSONDecodeError:
|
|
188
|
+
pass
|
|
189
|
+
|
|
190
|
+
def create_security_resources(self):
|
|
191
|
+
self.secrets_manager_secret = SecretsmanagerSecret(self, self.secret_name, name=f"{self.secret_name}")
|
|
192
|
+
self.resources["secretsmanager_secret"] = self.secrets_manager_secret
|
|
193
|
+
|
|
194
|
+
self.secrets.update({
|
|
195
|
+
"service_user_access_key": self.service_key.id,
|
|
196
|
+
"service_user_secret_key": self.service_key.secret,
|
|
197
|
+
"access_key": self.service_key.id,
|
|
198
|
+
"secret_access_key": self.service_key.secret,
|
|
199
|
+
"region_name": self.region,
|
|
200
|
+
"signature_version": self.default_signature_version
|
|
201
|
+
})
|
|
202
|
+
|
|
203
|
+
self.get_extra_secret_env()
|
|
204
|
+
|
|
205
|
+
if BaseLightsailArchitectureFlags.PRESERVE_EXISTING_SECRETS.value in self.flags:
|
|
206
|
+
self._create_secret_version_conditionally()
|
|
207
|
+
elif BaseLightsailArchitectureFlags.IGNORE_SECRET_CHANGES.value in self.flags:
|
|
208
|
+
self._create_secret_version_with_lifecycle_ignore()
|
|
209
|
+
else:
|
|
210
|
+
SecretsmanagerSecretVersion(
|
|
211
|
+
self,
|
|
212
|
+
self.secret_name + "_version",
|
|
213
|
+
secret_id=self.secrets_manager_secret.id,
|
|
214
|
+
secret_string=(json.dumps(self.secrets, indent=2, sort_keys=True) if self.secrets else None),
|
|
215
|
+
)
|
|
216
|
+
|
|
217
|
+
def _create_secret_version_conditionally(self):
|
|
218
|
+
try:
|
|
219
|
+
DataAwsSecretsmanagerSecretVersion(
|
|
220
|
+
self,
|
|
221
|
+
self.secret_name + "_existing_check",
|
|
222
|
+
secret_id=self.secrets_manager_secret.id,
|
|
223
|
+
version_stage="AWSCURRENT"
|
|
224
|
+
)
|
|
225
|
+
|
|
226
|
+
conditional_secret = SecretsmanagerSecretVersion(
|
|
227
|
+
self,
|
|
228
|
+
self.secret_name + "_version_conditional",
|
|
229
|
+
secret_id=self.secrets_manager_secret.id,
|
|
230
|
+
secret_string=json.dumps(self.secrets, indent=2, sort_keys=True) if self.secrets else None,
|
|
231
|
+
lifecycle={
|
|
232
|
+
"ignore_changes": ["secret_string"],
|
|
233
|
+
"create_before_destroy": False
|
|
234
|
+
}
|
|
235
|
+
)
|
|
236
|
+
|
|
237
|
+
conditional_secret.add_override(
|
|
238
|
+
"count",
|
|
239
|
+
"${length(try(jsondecode(data.aws_secretsmanager_secret_version." +
|
|
240
|
+
self.secret_name.replace("/", "_").replace("-", "_") + "_existing_check.secret_string), {})) == 0 ? 1 : 0}"
|
|
241
|
+
)
|
|
242
|
+
|
|
243
|
+
except Exception:
|
|
244
|
+
SecretsmanagerSecretVersion(
|
|
245
|
+
self,
|
|
246
|
+
self.secret_name + "_version_fallback",
|
|
247
|
+
secret_id=self.secrets_manager_secret.id,
|
|
248
|
+
secret_string=json.dumps(self.secrets, indent=2, sort_keys=True) if self.secrets else None,
|
|
249
|
+
)
|
|
250
|
+
|
|
251
|
+
def _create_secret_version_with_lifecycle_ignore(self):
|
|
252
|
+
secret_version = SecretsmanagerSecretVersion(
|
|
253
|
+
self,
|
|
254
|
+
self.secret_name + "_version_ignored",
|
|
255
|
+
secret_id=self.secrets_manager_secret.id,
|
|
256
|
+
secret_string=json.dumps(self.secrets, indent=2, sort_keys=True) if self.secrets else None,
|
|
257
|
+
)
|
|
258
|
+
|
|
259
|
+
secret_version.add_override("lifecycle", {
|
|
260
|
+
"ignore_changes": ["secret_string"]
|
|
261
|
+
})
|
|
262
|
+
|
|
263
|
+
def execute_post_apply_scripts(self):
|
|
264
|
+
if not self.post_apply_scripts:
|
|
265
|
+
return
|
|
266
|
+
|
|
267
|
+
dependencies = []
|
|
268
|
+
if hasattr(self, "secrets_manager_secret"):
|
|
269
|
+
dependencies.append(self.secrets_manager_secret)
|
|
270
|
+
|
|
271
|
+
for i, script in enumerate(self.post_apply_scripts):
|
|
272
|
+
script_resource = NullResource(
|
|
273
|
+
self,
|
|
274
|
+
f"post_apply_script_{i}",
|
|
275
|
+
depends_on=dependencies if dependencies else None
|
|
276
|
+
)
|
|
277
|
+
|
|
278
|
+
script_resource.add_override("provisioner", [{
|
|
279
|
+
"local-exec": {
|
|
280
|
+
"command": script,
|
|
281
|
+
"on_failure": "continue"
|
|
282
|
+
}
|
|
283
|
+
}])
|
|
284
|
+
|
|
285
|
+
def has_flag(self, flag_value):
|
|
286
|
+
return flag_value in self.flags
|
|
287
|
+
|
|
288
|
+
def clean_hyphens(self, text):
|
|
289
|
+
return text.replace("-", "_")
|
|
290
|
+
|
|
291
|
+
def properize_s3_bucketname(self, bucket_name):
|
|
292
|
+
clean_name = bucket_name.lower().replace("_", "-")
|
|
293
|
+
clean_name = clean_name.strip("-.")
|
|
294
|
+
return clean_name
|
|
295
|
+
|
|
296
|
+
def create_iam_outputs(self):
|
|
297
|
+
TerraformOutput(
|
|
298
|
+
self,
|
|
299
|
+
"iam_user_access_key",
|
|
300
|
+
value=self.service_key.id,
|
|
301
|
+
sensitive=True,
|
|
302
|
+
description="IAM user access key ID (sensitive)",
|
|
303
|
+
)
|
|
304
|
+
|
|
305
|
+
TerraformOutput(
|
|
306
|
+
self,
|
|
307
|
+
"iam_user_secret_key",
|
|
308
|
+
value=self.service_key.secret,
|
|
309
|
+
sensitive=True,
|
|
310
|
+
description="IAM user secret access key (sensitive)",
|
|
311
|
+
)
|
|
312
|
+
|
|
313
|
+
TerraformOutput(
|
|
314
|
+
self,
|
|
315
|
+
"secrets_manager_secret_name",
|
|
316
|
+
value=self.secret_name,
|
|
317
|
+
description="AWS Secrets Manager secret name containing all credentials",
|
|
318
|
+
)
|