aws/aws-cdk

(aws-core): Tags do not persist on constructs imported from another project (outside current dir)

franciszabala opened this issue · 3 comments

What is the problem?

Tags are not present on custom constructs that are imported from another project (outside of current project dir).

Consider the following file structure:

├── external-project
│   ├── lib
│   │   └── vpc.ts
│   ├── package.json
│   └── tsconfig.json
└── main-project
    ├── lib
    │   ├── index.ts
    │   ├── one-internal-one-external.ts
    │   └── vpc.ts
    ├── cdk.json
    ├── package.json
    └── tsconfig.json

I am importing a custom construct from external-project/lib/vpc.ts into main-project/lib/one-internal-one-external.ts, then running cdk synth from main-project/. This works without error however the generated CFN template does not include any tags on the externally imported resource. The internally imported custom constructs (e.g. main-project/lib/vpc.ts) are fine as they include the expected tags in the template.

Reproduction Steps

Update: check out this repo https://github.com/ryparker/aws-cdk-external-construct-tags

  1. Create 2 directories. cdk-test-lib and cdk-test-proj

  2. Go to cdk-test-lib and execute cdk init lib --language=typescript

  3. Once done, got to lib folder (it should contain index.ts) then create vpc.ts

  4. In vpc.ts, add this

    import { Construct } from "constructs";
    import { aws_ec2 as ec2 } from "aws-cdk-lib";
    
    export class VPCConstruct extends Construct {
    constructor(scope: Construct, id: string) {
    super(scope, id);
    
    const vpc = new ec2.Vpc(this, "using-external-lib", {
     cidr: "10.0.0.0/16",
     natGateways: 1,
     maxAzs: 3,
     subnetConfiguration: [
       {
         name: "private-subnet-1",
         subnetType: ec2.SubnetType.PRIVATE_WITH_NAT,
         cidrMask: 24,
       },
       {
         name: "public-subnet-1",
         subnetType: ec2.SubnetType.PUBLIC,
         cidrMask: 24,
       },
       {
         name: "isolated-subnet-1",
         subnetType: ec2.SubnetType.PRIVATE_ISOLATED,
         cidrMask: 28,
       },
     ],
    });
    }
    }
  5. Once done, go to the root dir of cdk-test-lib and execute npm run build.

  6. Navigate to cdk-test-proj, execute cdk init app --language=typescript and edit bin/cdk-test-proj.ts

#!/usr/bin/env node
import 'source-map-support/register';
import * as cdk from 'aws-cdk-lib';
import { VPCConstruct } from '../../cdk-test-lib/lib/vpc';

export class CdkStarterStack extends cdk.Stack {
        constructor(scope: cdk.App, id: string, props?: cdk.StackProps) {
        super(scope, id, props);
                new VPCConstruct(this, "using-external-lib");
        }

}

const app = new cdk.App();
new CdkStarterStack(app, 'CdkStarterStack');
  1. Save it, go to the root directory of cdk-test-proj and execute cdk ls
  2. Check out cdk.out/CdkStarterStack.template.json and see that the subnets don't have any tags.
  3. Sanity check: copy all the files in cdk-test-lib/lib and paste them to cdk-test-proj/ext_lib (create the ext_lib)
  4. Update the codes in bin/cdk-test-proj.ts to
#!/usr/bin/env node
import 'source-map-support/register';
import * as cdk from 'aws-cdk-lib';
import { VPCConstruct } from '../../cdk-test-lib/lib/vpc';
import { VPCConstruct as VPCInternal } from '../ext_lib/vpc';

export class CdkStarterStack extends cdk.Stack {
        constructor(scope: cdk.App, id: string, props?: cdk.StackProps) {
        super(scope, id, props);
                new VPCConstruct(this, "using-external-lib");
                new VPCInternal(this, "using-internal");
        }

}

const app = new cdk.App();
new CdkStarterStack(app, 'CdkStarterStack');
  1. Check out cdk.out/CdkStarterStack.template.json and see that the subnets for "using-internal" construct has its subnet with tag

What did you expect to happen?

I expect that regardless of the location of the library file, the synthesized template should be the same and have its subnets tagged. Subnets should have tag.

e.g. (except from template synthesized from above code)

usinginternalusingexternallibprivatesubnet1Subnet1RouteTableE9991CD9": {
      "Type": "AWS::EC2::RouteTable",
      "Properties": {
        "VpcId": {
          "Ref": "usinginternalusingexternallibEBC0D045"
        },
        "Tags": [
          {
            "Key": "Name",
            "Value": "CdkStarterStack/using-internal/using-external-lib/private-subnet-1Subnet1"
          }
        ]
      },
      "Metadata": {
        "aws:cdk:path": "CdkStarterStack/using-internal/using-external-lib/private-subnet-1Subnet1/RouteTable"
      }
    }

What actually happened?

Subnets don't have any tags. Even the VPC wasn't tagged when using library files outside the project directory

CDK CLI Version

2.12.0 (build c9786db)

Framework Version

No response

Node.js Version

v14.18.2

OS

Amazon Linux 2 (ami-02a45d709a415958a)

Language

Typescript

Language Version

No response

Other information

No response

I would like to add that it is not isolated to a VPC. Any custom construct being used as library outside the main project's directory will have its tags removed in the final cfn template. One work around is to copy the output of npm run build of the library folder and copy it to the main project folder and use that as the current library.

Also tested using symbolic link. It doesn't work either.

I was able to reproduce this and i've made a repo with the simplest reproduction. I've also created a couple of tests in that repo that validate this problem.

⚠️COMMENT VISIBILITY WARNING⚠️

Comments on closed issues are hard for our team to see.
If you need more assistance, please either tag a team member or open a new issue that references this one.
If you wish to keep having a conversation with other community members under this issue feel free to do so.