-
Notifications
You must be signed in to change notification settings - Fork 3.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Thoughts about resource IDs #1424
Comments
I’d want to go further and allow users to override the entire logical ID as a standard feature of constructs, with the caveat that if another resource ends up using the same ID, it’s up to the user to deal with that. This would have the same effect as the renaming “hack”, without needing to put the renames at the top of the stack. I see it as a the stack author signposting “I care about the root CFN resource this construct creates outside of this app” |
Yes. The current experience is not good enough. How about something like |
I think I can achieve want by providing my own naming system instead of using overrides everywhere but I have not tried it yet: https://awslabs.github.io/aws-cdk/refs/_aws-cdk_cdk.html?highlight=iaddressingscheme#logicalids But is there a way to get the actual Resource from inside of https://awslabs.github.io/aws-cdk/refs/_aws-cdk_cdk.html?highlight=iaddressingscheme#@aws-cdk/cdk.IAddressingScheme ? |
@eladb yep, I like that as a solution - better than having to put them all at the top of the stack. Potential use case is using CDK to generate templates for other teams to consume, and in that case "clean" resource IDs will matter. |
You can also do it by subclassing |
Thanks for the link to that. Unfortunately, it doesn't produce the behavior that I want. I'd like to strip or remove the auto-generated hash completely and allow for the logical id to be the id that I pass in the constructor of the resource. Basically, by overriding that method, I'm replacing the auto-generated hash with a hash of my own. It would be nice to disable that functionality altogether and let us assume the risk of collisions of logicalIds. |
I'm not sure what you mean by that... here's an example implementation: export class TestStack extends Stack {
protected allocateLogicalId(cfnElement: CfnElement): string {
return cfnElement.node.id;
}
} Note you need to be aware the separation between L2s (which you create explicitly) and L1 classes (those starting with the |
@skinny85 I've tried and tried to override this method with the code you provided but cfnElement.node.id just returns |
Yes. That's why I said:
You can try something like: protected allocateLogicalId(cfnElement: CfnElement): string {
const allScopes = cfnElement.node.scopes;
const stackIndex = allScopes.indexOf(cfnElement.stack);
const resourceScopes = allScopes.slice(stackIndex + 1);
return resourceScopes
.map(cons => cons.node.id)
.reduce((id, acc) => id + acc, '');
} to try and get rid of the hashes at the end of the logical IDs, while having a better chance of them being unique. Feel free to come up with your own algorithm as you see fit (for example, you might want to filter out generic IDs in the scopes like |
@skinny85 This is also an issue for me because I want to migrate to CDK from a simple CloudFormation template. I was under the impression that I would be able to describe a CDK stack which would generate a template similar to what I have. Because of the hash codes at the end of the names of the resources, the tables/buckets will be replaced, which I want to avoid. So, I don't want to generate a new unique logical id, I just want to use the one I gave in the constructor. |
@RoKish for that, you have a few options:
import * as cdk from '@aws-cdk/core';
import s3 = require('@aws-cdk/aws-s3');
export class MyStack extends cdk.Stack {
constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
new s3.CfnBucket(this, 'MyBucket')
}
} results in the template: Resources:
MyBucket:
Type: AWS::S3::Bucket
Metadata:
aws:cdk:path: MyStack/MyBucket
import * as cdk from '@aws-cdk/core';
import s3 = require('@aws-cdk/aws-s3');
export class MyStack extends cdk.Stack {
constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
const bucket = new s3.Bucket(this, 'MyBucket');
const cfnBucket = bucket.node.defaultChild as cdk.CfnResource;
cfnBucket.overrideLogicalId('MyBucket');
}
} results in the template: Resources:
MyBucket:
Type: AWS::S3::Bucket
UpdateReplacePolicy: Retain
DeletionPolicy: Retain
Metadata:
aws:cdk:path: MyStack/MyBucket/Resource Hope this helps! |
@skinny85 Thank you! The second should work for me. |
NP, glad I could help 🙂 |
@skinny85 How would you go about overriding allocateLogicalId in Python? Directly translating the code doesn't seem to work for me. |
This strategy continues to be a huge burden to an iterative development process. Creating a construct tree path of If we wrap I don't think any one solution works here, as has been discussed both have drawbacks to one side or another. I believe the default behavior should be that the ID (second parameter to all constructs) should be used as the logical ID, since they're typically hard coded or something hard coded plus some suffix for most end-user CDK code which is written. This leaves the issue of when "reusable components from multiple uncoordinated sources (i.e. 3rd parties, etc) are composed together". This is where the generation strategy which is the current default should be applied. The only time this issue comes into play again is when you wrap a construct which contains the third party construct and so happens way less frequently. Other strategies could be introduced potentially, but I believe that there is no one size fits all here and that each construct should decide how it sets the logical Ids for its children with the stack's default to being just returning the ID of the construct. |
Please add a functionality to use logical ids as resource ids (second parameter to all constructs). |
I second the request from @laimonassutkus. While I understand that the intent of the current ID scheme is meant to protect us from ourselves and ensure uniqueness, it's burdensome for those of us who don't need the extra guards. |
Meanwhile, this should help: class EnforceLegacyLogicalIds {
visit(element) {
if (element.node.id && element.node.defaultChild) {
element.node.defaultChild.overrideLogicalId(element.node.id);
}
}
}
export { EnforceLegacyLogicalIds }; Aspects.of(stack).add(new EnforceLegacyLogicalIds()); This will use constructs' IDs (the second parameter) as the logical ID. Besides the default child, other L1 constructs created by L2 and L3 constructs still need to be manually renamed. |
Terraform doesn't appear to have this issue. Cloudformation has so many issues that it makes no sense to have created CDK as a wrapper around it. Why not implement CDK as a wrapper around the API and drop the Cfn entirely? I appreciate this does nothing to help the discussion but I still find it very difficult to understand why anyone continues to use Cfn or CDK when there are such better alternatives. |
i also agree this create lot of issues in term of refactoring. when code start small with only one or two resources it look simple, but as the project grow over the years, if i ever want to create L3 construct this will trigger a new CDK path and completely create new resources. the solution i end up is create L3 construct but not actual construct. however this feel kinda bad design overall |
@abrooksv provided some feedback on resource IDs:
One of the main value propositions of the CDK is to enable composition of abstract cloud architectures into higher level abstractions, stacks and apps. Since CloudFormation's logical ID namespace is flat, we needed a way to make sure that resource IDs do not collide when reusable components from multiple uncoordinated sources (i.e. 3rd parties, etc) are composed together and synthesized into the same eventual CloudFormation template.
The natural solution would have been to use a delimited full path as the logical ID of each resource. However, this wouldn't have worked because CloudFormation puts rather strict constraints on logical IDs (only alphanumeric, limited in length).
We couldn't only add a suffix in the case where there is a conflict because this means that logical IDs would stop being stable: users might add a resource and suddenly some other resource would have their logical ID change. That's unacceptable behavior, since a change of logical IDs mean recreation of physical resources - definitely something we can't afford.
We've explored options with CloudFormation, and realized they have good reasons for these constraints and that changing them would not be possible without a huge breakage potential across their huge surface area. To overcome those limitations we came up with this model where we render logical IDs that contain both a “human” part and a hash of the full delimited path. The human part is rendered by concatenating the path and making it “nice” while the hash is basically a short MD5 hash, to ensure uniqueness within the template.
Yes, definitely not a very useful set of names. Would love to explore any ideas you might have on helping resolve this. Things that come to mind:
AwsToolkitJetBrainsReleasePreReleaseCloseReleaseGateCloseReleaseGateLambdaC27581F5. It is full of duplicates because the logical name for each construct should be descriptive enough on its own for it to understand what it does.
I'd argue that the “full logical ID” is:
AwsToolkitJetBrainsRelease/PreRelease/CloseReleaseGate/CloseReleaseGateLambda
and I wonder, for example, why not use shorter logical IDs? (I know it won't solve your problem, but maybe eliviate it in certain cases):
AwsToolkitJetBrains/PreRelease/CloseGate/Lambda*
As mentioned above, this will create a limitation that we can't have. Being able to use arbitrarily deep trees is probably the single most important part of the CDK, as this is how abstractions are composed.
Yes, that's a CloudFormation limitation that we can't really get around. If you use a physical name, CFN won't be able to replace it with another resource with the same physical name.
Again, this brings up the idea of perhaps allowing you to override only the human part of the logical ID... Definitely worth exploring this direction.
The text was updated successfully, but these errors were encountered: