hashicorp / terraform-cdk

Define infrastructure resources using programming constructs and provision them using HashiCorp Terraform
https://www.terraform.io/cdktf
Mozilla Public License 2.0
4.79k stars 443 forks source link

CLI: `cdktf synth --hcl` does not put child module resources inside of the child module #3604

Closed michaelnwani closed 2 months ago

michaelnwani commented 2 months ago

Expected Behavior

Given a stack that declares a Terraform module:

// main.ts

import { Construct } from "constructs";
import { App, TerraformStack } from "cdktf";
import * as ModuleA from "./modules/moduleA/main";

class MyStack extends TerraformStack {
  constructor(scope: Construct, id: string) {
    super(scope, id);

    new ModuleA.ModuleA(this, "module_a", {
      source: "./modules/moduleA",
    });
  }
}

const app = new App();
new MyStack(app, "mystack");
app.synth();

And the module contains a scoped resource:

// modules/moduleA/main.ts

import { TerraformModule, TerraformModuleConfig } from "cdktf";
import { Construct } from "constructs";
import { RandomProvider } from "@cdktf/provider-random/lib/provider";
import { Id as RandomId } from "@cdktf/provider-random/lib/id";

export class ModuleA extends TerraformModule {
  public constructor(
    scope: Construct,
    id: string,
    config: TerraformModuleConfig
  ) {
    super(scope, id, {
      ...config,
    });
    new RandomProvider(this, "default");
    new RandomId(this, "mod_a_resource_one", {
      byteLength: 2,
    });
  }
}

I expected to see the resource inside of the module in the synthesized hcl, because it was specified in a different scope, like this for example:

// cdktf.out/stacks/mystack/cdk.tf

terraform {
  required_providers {
    random = {
      version = "3.6.0"
      source  = "hashicorp/random"
    }
  }
  backend "local" {
    path = "<path>/terraform.mystack.tfstate"
  }
}

module "module_a" {
  source = "./assets/__cdktf_module_asset_26CE565C/AED96664F96F513147DECF4060B0C6AE"
}
// ./assets/__cdktf_module_asset_26CE565C/AED96664F96F513147DECF4060B0C6AE/main.tf

provider "random" {
}
resource "random_id" "mod_a_resource_one_0BDEBAC6" {
  byte_length = 2
}

Actual Behavior

The resource that was created in the terraform module isn't actually in the module:

// cdktf.out/stacks/mystack/cdk.tf

terraform {
  required_providers {
    random = {
      version = "3.6.0"
      source  = "hashicorp/random"
    }
  }
  backend "local" {
    path = "/<path>/terraform.mystack.tfstate"
  }
}

module "module_a" {
  source = "./assets/__cdktf_module_asset_26CE565C/AED96664F96F513147DECF4060B0C6AE"
}

provider "random" {
}
resource "random_id" "module_a_mod_a_resource_one_0BDEBAC6" {
  byte_length = 2
}

^ Also, under the ./assets/__cdktf_module_asset_26CE565C/AED96664F96F513147DECF4060B0C6AE directory, there's no generated terraform; just the modules/moduleA/main.ts file that was copied over.

Steps to Reproduce

  1. cdktf init --template="typescript" --providers "random"
  2. create a basic module like the one shown above
  3. add this module in cdktf.json
  4. reference the module in the stack
  5. run cdktf synth --hcl

Versions

{
  "dependencies": {
    "@cdktf/provider-random": "11.0.0",
    "cdktf": "^0.20.7",
    "cdktf-cli": "latest",
    "constructs": "^10.3.0",
    "typescript": "^5.2.2",
    "ts-node": "^10.9.2"
  }
}

cdktf.json:

{
  "language": "typescript",
  "app": "npx ts-node main.ts",
  "projectId": "f022e575-d4dc-4d88-8102-5518a7229da0",
  "sendCrashReports": "false",
  "terraformProviders": [],
  "terraformModules": [{ "source": "./modules/moduleA", "name": "module_a" }],
  "context": {}
}

tsconfig.json:

{
  "compilerOptions": {
    "alwaysStrict": true,
    "allowJs": true,
    "declaration": true,
    "experimentalDecorators": true,
    "esModuleInterop": true,
    "isolatedModules": true,
    "inlineSourceMap": true,
    "inlineSources": true,
    "lib": ["es2018"],
    "module": "CommonJS",
    "noEmitOnError": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "resolveJsonModule": true,
    "strict": true,
    "strictNullChecks": true,
    "strictPropertyInitialization": true,
    "stripInternal": true,
    "target": "ES2018",
    "incremental": true,
    "skipLibCheck": true
  },
  "include": [
    "main.ts",
    "**/*.ts",
    "modules/moduleA/main.ts"
  ],
  "exclude": [
    "node_modules",
    "cdktf.out"
  ]
}

Providers

{
  "@cdktf/provider-random": "11.0.0"
}

Community Note

DanielMSchmidt commented 2 months ago

Hey, this is a problem in how you use TerraformModule, it's a class intended for generated module bindings or references to HCL modules not an equivalent to a TerraformModule in HCL. For that you would generally want to use Construct. I created a PR that adds a validation to the module so that this kind of usage is prevented in the future.

github-actions[bot] commented 1 month ago

I'm going to lock this issue because it has been closed for 30 days. This helps our maintainers find and focus on the active issues. If you've found a problem that seems similar to this, please open a new issue and complete the issue template so we can capture all the details necessary to investigate further.