Closed eladb closed 5 years ago
@eladb status? I need nested stack support before I can start using this cdk really.
This should not be too hard to support through assets.
A bit more context/direction/pointers/ideas:
As a user, I'd like to be able to be able to define a nested stack like any other stack in the CDK:
class SmallStack extends Stack {
// ...
}
class BigStack extends Stack {
constructor(p: Construct, id: string) {
new SmallStack(this, 'NestedStack');
}
}
const app = new App();
new BigStack(app, 'my-awesome-stack');
app.run();
An instance of SmallStack
has been added as a child for BigStack
. My expected behavior for this would be that SmallStack
will be modeled as a nested in the my-awesome-app
template.
This technically means that:
my-awesome-app
(like today) and the other for the nested stack.my-awesome-app
will include an AWS::CloudFormation::Stack
resource with TemplateURL
assigned from a CloudFormation Parameter.my-awesome-app
, the toolkit should upload the nested stack template to S3 and then assign the location of the uploaded template to the parameter wired to the TemplateURL
.Assets: the protocol between the CDK and the toolkit (defined under the cx-api module) allows CDK apps to emit metadata entries that instruct the toolkit to package and deploy various types of assets (s3 files, s3 directories, docker images) and then reference them through CloudFormation parameters when the stack is deployed, which is effectively what we need here.
This has implications on how Stacks
are instantiated, render toCloudformation
and import Output
values.
Minor:
Construct
, not just an App
.StackProps.env
is irrelevant - nested stacks are always deployed in the same account/region pair.Stack
instance when used as a root vs nested stack?If it's a nested stack, we want to render both its template and the AWS::CloudFormation::Stack
resource, passing in parameter values from the parent. Can/should a stack be designed for both use-cases?
interface ChildStackProps extends cdk.StackProps {
bucketArn?: string; // what does it mean to specify this as a root stack?
}
class ChildStack extends Stack {
constructor(parent: cdk.Construct, name: string, props: ChildStackProps = {}) {
super(parent, name, props);
this.bucketParameter = new cdk.Parameter(this, 'BucketArn', {
type: 'AWS::S3::Bucket'
});
if (props.bucketArn) {
// how to 'accept' the bucketArn when used as a nested stack?
}
}
// how does toCloudformation distinguish its behavior?
}
Output
value with Fn::GetAtt Outputs.NestedStackOutputName
, not Fn::ImportValue
.Do we expect developers to use a different import API when using nested stacks, or re-purpose the existing import and XXXRef
convention? Passing around BucketRef
works well when the stacks are all root stacks, but the behavior is different for nested stacks, so developers must be aware of where the value came from.
class ParentStack extends Stack {
constructor(parent: cdk.Construct, name: string) {
super(parent, name);
this.child = new ChildStack(this, 'Child');
// Can I do this? It will use Fn::ImportValue instead of Fn::GetAtt
const bucket = s3.BucketRef.import(this, 'Bucket', this.child.bucketProps);
}
}
class ChildStack extends Stack {
public readonly bucketProps: s3.BucketRefProps;
constructor(parent: cdk.Construct, name: string) {
super(parent, name);
this.bucketProps = new s3.Bucket(this, 'Bucket').export();
}
}
I don't know if two child stacks can import values from each-other, or if they must be proxied through Fn::GetAtt
and Parameters
via a common parent stack:
class ChildStack extends Stack {
public readonly bucketProps: s3.BucketRefProps;
constructor(parent: cdk.Construct, name: string) {
super(parent, name);
this.bucketProps = new s3.Bucket(this, 'Bucket').export();
}
}
class ChildStack2 extends Stack {
constructor(parent: cdk.Construct, name: string, child: ChildStack) {
super(parent, name);
// will this work?
s3.BucketRef.import(this, 'Bucket', child.bucketProps);
}
}
Good points. All the cross reference aspects should probably be designed in conjunction with @rix0rrr's work on #1324. Rico proposes to eliminate the need for explicit import/export, which might serve to identify which type of reference this is: between two root stacks, between two nested stacks or between a nested stack and it's parent.
The bucketArn
input prop example you provided above may be make complete sense as a root stack as well as a nested stack. A stack subclass is a reusable component that can be instantiated many times within the same app or in different apps, and it may accept inputs. Bear in mind that there should probably be a distinction between a concrete value for bucketArn
and a value that includes tokens (cdk.unresolved(props.bucketArn)
returns true
). In the former case, the value would just propagate naturally to the nested/root stack without any need to utilize deploy-time resolution via CloudFormation parameters. In the latter, tokens must first be resolved (at deploy-time) and only then they can be used, so "magic" need to happen.
All the issues you bring up are relevant and important. However, try to design this in layers. How can we provide support for nested stack with minimal magic and maximum control for the user (can we provide only runtime errors if users attempt to cross the boundaries in the wrong way?). Then, we can see how we can sprinkle magic on top...
Meta: would probably be easier to discuss this design via PR under design/*.md
(see Design Process).
Keep in mind that users would expect cdk diff
to traverse into the nested stacks and provide a complete list of differences of the application. This is even not supported on CloudFormation yet.
@tyron yeah it sure isn't, and not only that it basically always returns changes even when nothing has changed.
as a workaround I created some utilities here https://github.com/node-cfn
it implements a poor-mans diff
, supporting nested stacks - at least as best as I can. it's probably not as precise as a solution would need to be to meet aws standards; it's a library and I use it in a serverless plugin (https://github.com/dougmoscrop/serverless-plugin-bootstrap) which is responsible for representing the diff textually
I would love to switch that plugin to use the cdk under the hood instead if it could support nested stacks. I don't know if any of my work there could be useful.
Approach wise, it topologically sorts the root stack and then runs a diff on each nested stack, and marks changes if it detects a change in things like the template URL (which is always hashed), or any properties in actual templates themselves, and then any thing that depends on a thing that changed gets marked as changed too (so it can possibly err on the side of falsely marking something as changed, but then the visual diff will reveal that nothing really changed). But it avoids false positives when there actually are no changes.
it doesn't currently support imports or nested stacks within nested stacks, though those could be added.
@eladb
Hi everyone,
Is there an update on the status of this issue? I'm using CDK for a rather complex set of stacks, which will probably go over the resource limit of CloudFormation, so I'm wondering if I need to foresee issues or if there is already an out-of-the-box solution from CDK.
@bverhoeve i am working on official support for nested stacks in #2821 but I am not even sure you will need this. You can define any number of stacks in the CDK and freely reference resources between them. The cdk will automatically synthesis exports and imports (as long as they are in the same environment). This is mostly the same as nested stacks (some would say superior since there could be more complex relationships).
@eladb thank you for the update, this sounds like great news!
So if I get this correctly, this would mean that I can define multiple Stack
objects on which I can define up to 200 CloudFormation resources and CDK will split in multiple CloudFormation templates and take care of the imports/exports?
I think nested stacks could be useful for our use case:
I figure that if we do this with nested stacks then those stacks will manage the creation or new alarms and deletion of the old ones in an automated way.
@eladb FWIW the resource_stack link you gave is great, but it took me quite awhile to find it (I nearly opened an issue asking if this capability [cross stack references] was even supported, b/c it really seems like it should be, but afaict none of the howtos pointed it out, and none of the readmes/etc. I'd found explicitly used that approach, and after combing through issues ended up here.)
Would be great if this multiple stack how to, which is currently just "I'll make multiple versions of the same stack" had a little link at the bottom for "btw if you want to do cross-stack references, see [the link you just gave]".
@eladb I see that the PR #2821 you gave mentioned earlier has been closed. Does it mean that the development has been put on hold? I think it would be incredibly useful for constructs such as DynamoDB Global Tables, which generate stacks and at the moment yield parameter errors when used inside a stack.
@eladb Is there any update on the status for this? The PR has been re-opened which sounds like good news. Is it reasonable to expect nested stacks released within October or November?
Despite the suggestion for using multiple top-level stacks, nested stacks are a completely different ball game within CloudFormation.
The main problem with multiple top-level stacks is that if a stack fails to update, you do not get the waterfall rollback of all the stacks that have been changed. In some cases, this can lead to really big issues!
Hey @kbessas, I hope I'll be able to follow up on this soon. Generally I'd like to land this for the exact reasons you described.
I'm a bit confused now. Maybe my confusion will help someone...
Currently (1.9.0
) if I try nested stacks (
and cdk synth
that, the top level ns-dev.template.json
is just {}
— so I assume it is not possible right now. It cannot deploy.
I tried to use a Construct as the main (ns-dev) that contains the sub-stacks. This doesn't seem to work either:
ns-cdk-devops ❯ cdk deploy
Since this app includes more than a single stack, specify which stacks to use (wildcards are supported)
Stacks: nsdevnetE5343A5C nsdevappcacheEE591D45 nsdevdb84DB0F47 nsdevappecs4B1E5DD6 nsdevappcdn9B80D361
ns-cdk-devops ❯ cdk deploy nsdevnetE5343A5C
nsdevnetE5343A5C: deploying...
nsdevnetE5343A5C: creating CloudFormation changeset...
❌ nsdevnetE5343A5C failed: ValidationError: Stack [nsdevnetE5343A5C] does not exist
Stack [nsdevnetE5343A5C] does not exist
I guess "does not exist" means that the template it isn't uploaded to aws yet?
So at the moment, multiple stacks can only be up at the app level, and any sub functionality inside those should be written with Construct. Is that about right?
@eladb status? I need nested stack support before I can start using this cdk really.
One year later it's here 💪
Well the issue is closed... we merged this feature yesterday and expect it to be released with the next release (up to two weeks).
Hi, I'm trying to create a CloudFormation template which uses nested stacks.
First of all I have to say that I don't use cdk deploy
in order to deploy my template. What I do is generate .json
templates inside the code calling app.synth()
and executing .js
file. (Those .json
are going to be "manually" stored in a S3 which belongs to a different account from the one which is deploying the main template).
I've made some tests using NestedStack construct and CfnStack class and what I found out is:
NestedStack: Divides each Stack (created by extending cloudformation.NestedStack
) in a different file .nested.template.json
and on the main template .template.json
are written all references to those stacks AWS::CloudFormation::Stack
.
CfnStack: Creates one single file which includes all resources created on those Stacks (extending cloudformation.CfnStack
) and the Stack itself AWS::CloudFormation::Stack
.
I'm mostly sure that my template will have more than 200 CF resources so I need to use nested stacks, but the problems are:
NestedStack:
templateUrl
where the templates will be storedCfnStack:
templateUrl
(I have to insert the S3 Bucket url because as I mentioned before they will be stored in a specific location.)
I've seen https://github.com/aws/aws-cdk/issues/239#issuecomment-518978759 but does not solve my problem.
FYI I'm using @aws-cdk (v1.15.0) and Typescript
It should be easy to define nested stacks in the CDK.
This requires a good story behind uploading synthesized templates to S3 and "plugging in" the URL of the nested stack into the parent stack (see #233).
We should have a story on how to reference resources from the parent child in the child stack (i.e. using nested stack "Parameters") and how to reference resources from the child stack in the parent stack (i.e. using "Outputs").