How to iterate over objects made with an iterator?

The docs for iterators demonstrate how to create several s3 buckets:

import { s3 } from "@cdktf/provider-aws";
import { TerraformIterator, TerraformVariable } from "cdktf";

const list = new TerraformVariable(this, "list", {
  type: "list(string)",
});

const iterator = TerraformIterator.fromList(list.listValue);

const s3Bucket = new s3.Bucket(this, "bucket", {
  forEach: iterator,
  name: iterator.value,
});

How can I then iterate over all of these s3 buckets? As a contrived example, suppose I want to create an EC2 instance for each bucket and pass the bucket ID as an environment variable to it’s respective EC2 instance, how can I do that? From what I can tell, s3Bucket itself isn’t iterable, does it expose a property that allows users to iterate?

EDIT: to give a more concrete example of what I’m facing right now, I create a number of subnets like so:

    const dataAwsAvailabilityZonesAll =
      new aws.datasources.DataAwsAvailabilityZones(this, "all", {});
    const zoneNames = TerraformIterator.fromList(dataAwsAvailabilityZonesAll.names)


    new aws.vpc.Subnet(this, "priv_subnet", {
      forEach: zoneNames,
      availabilityZone: zoneNames.value,
      cidrBlock: Fn.cidrsubnet(clusterVpc.cidrBlock, 8, Fn.index(dataAwsAvailabilityZonesAll.names, zoneNames.value) + dataAwsAvailabilityZonesAll.names.length),
      mapPublicIpOnLaunch: false,
      vpcId: "${aws_vpc.vpc.id}",
    });

and now I want to make a route table association

    const privateRouteTable = new aws.vpc.RouteTable(
      this,
      "privateRouteTable",
      {
        vpcId: clusterVpc.id,
        route: [
          {
            cidrBlock: "0.0.0.0/0",
            natGatewayId: natGateway.id,
          },
        ],
        tags: {
          name: `privateRouteTable-${environment}`,
        },
      }
    );

    const awsRouteTableAssociationPrivate = new aws.vpc.RouteTableAssociation(
      this,
      "private",
      {
        routeTableId: privateRouteTable.id,
        subnetId:        // <---------- how do I fill this in? I need to somehow iterate over each subnet created earlier
      }
    );

but I’m not sure how to add the ID of each subnet in priv_subnet to awsRouteTableAssociationPrivate

Currently iterators won’t really help with this. You’ll need to use escape hatches. There are a couple older examples of this, but unfortunately nothing that is up to date.

I’ve created Support accessing resources created by an iterator · Issue #2024 · hashicorp/terraform-cdk · GitHub to track adding more built in functionality.

Your cidrBlock calculation will also run into issues since the amount of availability zones isn’t know at application runtime. Count iterators and count meta-argument · Issue #2021 · hashicorp/terraform-cdk · GitHub tracks what will likely be a better method for using that.

1 Like

ah, thanks for the info!

Regarding the cidrBlock calculation - does the problem stem from the usage of TerraformIterator.fromList which operates at application runtime and not at execution time?

Stepping back a bit, I’m currently trying to upgrade from cdk 0.8.6 to cdk 0.12 and this was one of the issues I ran into. In our old code we were using escape hatches like so:

    const privateSubnet = new aws.vpc.Subnet(this, "priv_subnet", {
      availabilityZone: `\${${dataAwsAvailabilityZonesAll.fqn}.names[count.index]}`,
      cidrBlock:
        "${cidrsubnet(aws_vpc.vpc.cidr_block, 8, count.index + length(data.aws_availability_zones.all.names))}",
      mapPublicIpOnLaunch: false,
      tags: {
        name: environment + "-private-subnetroutei-${(count.index + 1)}",
        ["kubernetes.io/cluster/" + eksClusterName]: "shared",
        "kubernetes.io/role/internal-elb": "1",
        environment: environment,
        public: "false",
      },
      vpcId: "${aws_vpc.vpc.id}",
    });
    privateSubnet.addOverride(
      "count",
      `\${length(${dataAwsAvailabilityZonesAll.fqn}.names)}`
    );

However, it seems that using .fqn in version 0.12 isn’t well supported. This comment suggests a possible solution but it’s not clear how to use that with addOverride. It’s also not clear how to do something like \${${dataAwsAvailabilityZonesAll.fqn}.names[count.index]} which requires using .fqn correctly and using count.index - any chance you can help with this?

For the count you shouldn’t need to use an override. Something like count: Fn.lenghtOf(dataAwsAvailabilityZonesAll.names) should work.

For the other piece:
availabilityZone: Fn.element(dataAwsAvailabilityZonesAll.names, Token.asNumber("count.index")) should work.

I am facing the same issue while trying to associate a list of subnets to a route table

Anyone have an example of an escape hatch that I can refer to create route table associaiton with subnets created via iterator?