I have a subnet declaration like the following which creates one subnet for each AWS AZ in us-west-2
:
const dataAwsAvailabilityZonesAll =
new aws.datasources.DataAwsAvailabilityZones(this, "all", {});
const zoneNames = Fn.lookup(
dataAwsAvailabilityZonesAll.fqn,
"names",
undefined
);
const availabilityZone = Fn.element(
zoneNames,
Token.asNumber("count.index")
);
const publicSubnet = new aws.vpc.Subnet(this, "pub_subnet", {
availabilityZone,
cidrBlock: "${cidrsubnet(aws_vpc.vpc.cidr_block, 8, count.index)}",
mapPublicIpOnLaunch: true,
tags: {
name: environment + "-public-subnet-${(count.index + 1)}",
["kubernetes.io/cluster/" + eksClusterName]: "shared",
"kubernetes.io/role/elb": "1",
environment: environment,
public: "true",
},
vpcId: "${aws_vpc.vpc.id}",
});
publicSubnet.addOverride("count", Fn.lengthOf(zoneNames));
I'd like to refactor this to get rid of the usages of Fn.count
, Fn.lookup
, count.index
, etc. Instead, I'd like to have direct references to each subnet in typescript. I've tried doing the following:
const testSubnets = []
for (let i = 0; i++; i < Fn.lengthOf(zoneNames)) {
const s = Fn.element(zoneNames, i)
testSubnets.push(new aws.vpc.Subnet(this, `subnet-${s}`, {
availabilityZone: s,
cidrBlock:
`\${cidrsubnet(aws_vpc.vpc.cidr_block, 4, ${i + 3*dataAwsAvailabilityZonesAll.names.length})}`,
mapPublicIpOnLaunch: true,
tags: {
name: environment + `${environment}-large-public-subnet-${i}`,
["kubernetes.io/cluster/" + eksClusterName]: "shared",
"kubernetes.io/role/elb": "1",
environment: environment,
public: "true",
},
vpcId: clusterVpc.id
}))
}
but this alternative doesn't create any subnets at all. I've also tried using dataAwsAvailabilityZonesAll.names.length
and dataAwsAvailabilityZonesAll.names
directly but couldn't figure those out either. It seems that dataAwsAvailabilityZonesAll.names
is empty at runtime and that it doesn't resolve until much later in the CDKTF lifecycle.
How can I get the names of each AZ in a given region when using CDKTF? Instead of trying to automate each AZ, should I declare the AZs as a constant and use those when creating a subnet?
When working with CDK for Terraform it's important to be mindful of which code is really running at synth time in your source language vs. what is just getting translated into equivalent code in the Terraform language for Terraform to deal with at runtime.
Data resources will only be read by Terraform itself when generating the plan or during the apply phase, depending on the dependencies of the
data
block in the configuration. Therefore you can't interact with the results of the data resource directly in your generation code. Instead, you need to ask CDK for Terraform to generate code that will make use of that result.For this to work, you'll need to set the special
forEach
property when defining youraws.vpc.Subnet
object, which in turn tells CDK for Terraform to generate a Terraformresource "aws_subnet"
block with thefor_each
meta-argument set.To populate that from a dynamically-loaded collection, you'll need to use CDK for Terraform Iterators, which are an abstraction over dynamic collections ready to be passed in to
forEach
.For example:
Notice that this
aws.vpc.Subnet
now represents multiple subnets at once: one for each element ofzoneNames
. Therefore the properties of its definition object must represent rules for deriving the arguments from the current element, rather than hard-coded values. If you will use escape hatches as part of those rules, you can useeach.value
to refer to the availability zone of the current element; that Terraform expression is equivalent tozoneIterator.value
in CDK for Terraform.According to the latest discussions I could find on the subject there doesn't seem to be a first-class way to refer to individual instances of a resource declared with
forEach
in this way, so this is a situation where "escape hatches" are required. The complexity of that can be minimized by placing the escape hatch into a local value and then accessing that local value later in your code: