Greetings,
We would like to have one Stack that’s initialised with one AWS account and a list of region names and then the idea is that whatever Constracts get scoped into it, they’d get regionally replicated on the cloud.
What I need help with is:
- Resource constructs accept a
provider
parameter - singular. Does that mean that we can not achieve the pattern outlined above; with one-to-many mapping between the Constructs within a Stack and multiple regions? Is this the only way to dictate the provider to the Constructs for the purposes of deploying it regionally? - I tried to fetch the current actual region at deploy-time using a
DataAwsRegion
. I believe need to use the region name in deriving the resourceid
, that’s why I’m trying to look it up in interpolated form during deployment. But I get aRuntimeError: You cannot use a Token (e.g. a reference to an attribute) as the id of a construct
. That leads me to thinking that perhaps I should reconsider my pattern. - In general, how would one achieve the pattern outlined above with DRY code?
The code for the relevant class is below. I would appreciate any help. Thanks in advance.
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):
stack_id = generate_stack_id(scope.name, name, environment.account)
super().__init__(scope, id=stack_id)
self.account = environment.account
if self.account is None:
Annotations.of(self).add_error(f"Invalid account name '{self.account}'. Expected one of: {ALL_AWS_ACCOUNTS}.")
else:
self.account_id = AWS_ACCOUNT_IDS[self.account]
self.regions = environment.regions
if self.regions is None:
Annotations.of(self).add_error(f"Invalid list of regions given. Expected a subset of: {ALL_AWS_REGIONS}.")
self.name = name
self.env_suffix = environment.suffix
self.spec = spec
self.role_arn = f"arn:aws:iam::{self.account_id}:role/AdminAccessRole"
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.providers = {}
for region in self.regions:
provider_params = {
"scope": self,
"id": region,
"region": region,
"assume_role": {"role_arn": self.role_arn, "duration": "1h"}
}
if region != DEFAULTS["aws-region"]:
provider_params["alias"] = region
self.providers[region] = cdktf_aws.provider.AwsProvider(
**provider_params
)
region_lookup = cdktf_aws.data_aws_region.DataAwsRegion(
self,
"data-source-aws-region-" + generate_random_string()
)
# the below line gets: RuntimeError: You cannot use a Token (e.g. a reference to an attribute) as the id of a construct
# self.region = region_lookup.get_string_attribute("name")
edit: I should probably add that everything was working when I used to have single-region stacks with one (non-aliased) provider per Stack. The challenge is doing it in a way such that each Stack can be paired with multiple regions instead of creating many stacks per region.