Hi Daniel,
Thanks for the response. I appreciate you mentioning these points; they will be very useful.
The way I inserted a conditional in the constructor of the Construct was just incorrect… So now I have this working code that does the skipping at the top level (main.py) . Here ECRRepository is regional, IAMUser* aren’t:
class AppStack(AWSStack):
def __init__(self, scope: Construct, name: str, environment: AWSEnvironment, spec: dict):
super().__init__(scope, name, environment, spec)
ECRRepository(self, item_name + "-ecr-repository")
if self.region == DEFAULTS["aws-region"]:
IAMUserApp(self, item_name + "-app-user")
IAMUserDeploy(self, item_name + "-deployer-user")
The ‘library’ is very simple; and I no longer have that CustomConstruct base class. At this point It is not open source, however I can share these two classes whereby I attempted to capture the boilerplate. An AWSStack is basically a Stack that is coupled with an AWSEnvironment.
class AWSEnvironment:
"""
Data class representing an AWS environment. This is passed into an
AWSStack to associate it with an AWS region & account. Parameters:
account: str : The AWS Account name such as 'development'
region: str : The AWS Region name such as 'us-east-1'
"""
def __init__(self, account: str, region: str):
self.account = account.strip()
if self.account not in ALL_AWS_ACCOUNTS:
print("Invalid account name", self.account, ". Expected one of:", ALL_AWS_ACCOUNTS)
self.suffix = AWS_ACCOUNT_SUFFIXES[self.account]
self.region = region.strip()
if not re.match(r'^[a-z]{2}\-[a-z]+\-[0-9]$', self.region):
print("Invalid region name", self.region, ". Example: us-east-1")
class AWSStack(TerraformStack):
"""
Wraps TerraformStack, implementing most of the boilerplate,
essential configuration and validations for a CDK stack on AWS
"""
def __init__(self, scope: Construct, name: str,
environment: AWSEnvironment, spec: dict):
super().__init__(scope, id=kebab2CamelCase(name))
self.name = name
self.account = environment.account
self.region = environment.region
self.env_suffix = environment.suffix
self.spec = spec
try:
self.account_id = AWS_ACCOUNT_IDS[self.account]
except KeyError:
print(self.account, "is not a valid AWS account. Expected one of:", ALL_AWS_ACCOUNTS)
sys.exit(1)
self.role_arn = f"arn:aws:iam::{self.account_id}:role/OrgAccountAccessRole"
self.state_bucket_name = STATE_BUCKETS[self.account]
self.state_file_name = f"cdktf/{scope.name}/{self.name}.state"
self.backend = S3Backend(
self,
bucket=self.state_bucket_name,
region=DEFAULTS["aws-region"],
key=self.state_file_name,
role_arn=self.role_arn,
encrypt=True,
dynamodb_table="terraform-state-lock"
)
self.provider = AwsProvider(
self,
"AWS",
region=self.region,
assume_role={"role_arn": self.role_arn, "duration": "1h"}
)
I will be applying your points #2 and #3 to refactor these print
s at least. Wouldn’t be suprised to find more existing classes in the cdktf library that I could have used.
In its current state the CDKTF API docs for Python are all in one big page; I didn’t see a neat list of classes for example. TBH this has made using documentation a bit of a challenge as well.
Cheers