1

I am using cdktf for typescript and I am facing the situation that I only want to add a resource under a given condition. To solve this problem, I found a couple of questions on SO and I figured that we might want to use count in order to solve this problem.

So I came up with this code:

    const zipBuild = new DataArchiveFile(this, 'zipBuild', {
      type: 'zip',
      sourceDir: asset.path,
      outputPath: join(asset.path, 'dist.zip'),
      excludes: ['dist.zip'], // don't zip the zip
    });
    zipBuild.addOverride("count", "${var.my_condition} ? 1 : 0");

    const build = new GoogleStorageBucketObject(this, 'build', {
      name: `gcp-fancy-bucket-name/${zipBuild.outputSha}.zip`,
      bucket: deployBucket.stringValue,
      source: zipBuild.outputPath, // <<<< This is the offending line
      timeouts: {
        create: '15m',
        update: '15m',
      },
    });
    zipBuild.addOverride("count", "${var.my_condition} ? 1 : 0");

But when I want to synthesise this code to terraform, I get an error during the terraform validate step:

Error validating cdktf {
  err: Error: Command failed: terraform validate -no-color
  
  Error: Missing resource instance key
  
    on cdk.tf.json line 122, in resource.google_storage_bucket_object.build_088E518B:
   122:         "name": "gcp-fancy-bucket-name/${data.archive_file.zipBuild_FD7BC769.output_sha}.zip",
  
  Because data.archive_file.zipBuild_FD7BC769 has "count"
  set, its attributes must be accessed on specific instances.
  
  For example, to correlate with indices of a referring resource, use:
      data.archive_file.zipBuild_FD7BC769[count.index]
  
  Error: Missing resource instance key
  
    on cdk.tf.json line 123, in resource.google_storage_bucket_object.build_088E518B:
   123:         "source": "${data.archive_file.zipBuild_FD7BC769.output_path}",
  
  Because data.archive_file.zipBuild_FD7BC769 has "count"
  set, its attributes must be accessed on specific instances.
  
  For example, to correlate with indices of a referring resource, use:
      data.archive_file.zipBuild_FD7BC769[count.index]

I checked the cdktf code of DataArchiveFile, TerraformDataSource and TerraformElement but I couldn't find a method to access zipBuild.outputPath (or another property) for an array object, rather than the single object.

Does anyone used count before in combination with cdktf and knows how to handle it?

Also I tried this, but this also failed:

    const build = new GoogleStorageBucketObject(this, 'build', {
      name: `gcp-fancy-bucket-name/${zipBuild.outputSha}.zip`,
      bucket: deployBucket.stringValue,
      source: "${" + zipBuild.fqn + ".outputPath[count.index]}",
      timeouts: {
        create: '15m',
        update: '15m',
      },
    });

because the .fqn will give me an additional set of ${}:

\\"bucket\\": \\"${${google_storage_bucket_object.build}.bucket[count.index]}\\",
unterstein
  • 79
  • 5
  • Have you considered using native enumerable iteration in Typescript as opposed to `count` in Terraform? It would probably be easier to achieve your goal here. – Matthew Schuchard Jan 05 '23 at 16:03
  • Oh, I forgot to explain: We are using cdktf to render terraform with `TerraformVariable`. We call the rendered Terraform stacks later with the terraform cli and we are not running the apply from typescript. So we can't use typescript language features here because they would not be translated to the terraform stack. – unterstein Jan 06 '23 at 15:23

3 Answers3

1

I worked out how one array element can be accessed in this context:

The key was a combination from propertyAccess and Fn.lookup :-)

    const zipBuild = new DataArchiveFile(this, 'zipBuild', {
      type: 'zip',
      sourceDir: asset.path,
      outputPath: join(asset.path, 'dist.zip'),
      excludes: ['dist.zip'], // don't zip the zip
    });
    zipBuild.addOverride("count", "${var.my_condition} ? 1 : 0");

    const realZipBuild = propertyAccess(zipBuild.fqn, [0]);
    const zipBuildOutputPath = Fn.lookup(realZipBuild, 'output_path', '') as string;

    const build = new GoogleStorageBucketObject(this, 'build', {
      name: `gcp-fancy-bucket-name/${zipBuild.outputSha}.zip`,
      bucket: deployBucket.stringValue,
      source: zipBuildOutputPath,
      timeouts: {
        create: '15m',
        update: '15m',
      },
    });
    zipBuild.addOverride("count", "${var.my_condition} ? 1 : 0");
unterstein
  • 79
  • 5
0

Given that conditionals and looping are one of the primary reasons to use cdktf, my suggestion would be to use standard Terraform for your project.

  • We are enjoying cdktf for exactly this reason and we do have a lot of code that takes advantage from it, it's just this one occurrence that is different to the others. Switching away from it, just based on this one special use case, seems a little bit too much to be honest. – unterstein Jan 09 '23 at 13:53
  • You can also call native Terraform from cdktf if that helps your situation at all. – Kerry Wilson Jan 10 '23 at 14:06
0

I have worked with cdktf and count using typescript. The best way i was able to tackle the count was to use interpolation to get desired value. Example

 new VolumeAttachment(this,"example-disk-attachment",{
  deviceName: "/dev/sdh",
  volumeId : `\${element(aws_ebs_volume.example-disk.*.id, count.index)}`,
  instanceId: `\${element(aws_instance.example-machine.*.id, count.index)}`,
  dependsOn : [ec2Instance,exampleVolume],
  count: networkCount
})

This is just an example how i used interpolation with terraform cdk to make count work. I can share more context to this if you want as it worked perfectly fine for me.