10

Would it be possible to have two CDK Apps in the same project, something like this:

from aws_cdk import core

from stack1 import Stack1
from stack2 import Stack2

app1 = core.App()
Stack1(app1, "CDK1")
app1.synth()

app2 = core.App()
Stack2(app2, "CDK2")
app2.synth()

And deploy them? Synchronously/Asynchronously?

Would it be possible to reference some resources from one app in the other one?

Laimonas Sutkus
  • 3,247
  • 2
  • 26
  • 47

4 Answers4

10

Yes you can have multiple applications in a CDK project, but there are some serious caveats.

  1. A CDK process can only synth/deploy one app at a time.

  2. They cannot be defined in the same file.

  3. They cannot directly reference each other's resources.

To put this in perspective, each app is functionally isolated from each other and it is roughly equivalent to having two separate CDK projects just sharing the same codebase, so the use cases for this are limited.

The only way for them to share resources is either to extract it to an additional common app that must be deployed first, or for you to store the ARN of that resource in something (e.g., Parameter Store), and load it at run time. You cannot assume that the resource will exist as one of the apps may not have been deployed yet, and if you import the resource into your Stack directly, you've defeated the whole point of splitting them apart.

That is to say, this is ok:

stack1.lambda:

    from ssm_parameter_store import SSMParameterStore

    store = SSMParameterStore(prefix='/Prod')
    ssn_arn = store['stack2.sns']
    if !ssn_arn
        // Doesn't matter
        return
    try:
        sns.publish(ssn_arn, 'something')
    except:
        // Doesn't matter

But if it's critical to stack1 that a resource from stack2 exists, or you want to import a stack2 resource into stack1, then you either need to do a third split of all the common resources: common-resources.app.py, or there's no point splitting them.


We do this a lot in our projects, with one app creating a CodePipeline that automatically deploys the other app. However, we only do this because we prefer the pipeline lives next to the code it is deploying and it would be equally valid to extract it into an entirely new project.


If you want to do this, you need to do:

app1.py:

from aws_cdk import core

from stack1 import Stack1

app1 = core.App()
Stack1(app1, "CDK1")
app1.synth()

app2.py:

from aws_cdk import core

from stack2 import Stack2

app2 = core.App()
Stack2(app2, "CDK2")
app2.synth()

You then deploy this by running in parallel or sequentially:

  • cdk deploy --app "python app1.py"
  • cdk deploy --app "python app2.py"
Digital Deception
  • 2,677
  • 2
  • 15
  • 24
  • 2
    It looks like a valid usage for apps could be to deploy to separate environments stages. You can use the App as a bundling of stacks and then when you instantiate it you can pass `env: {account: '1111111111', region: 'eu-west-1'}` in the App props. Thoughts? – Orestis Kapar Jan 18 '22 at 20:22
  • 1
    That's kind of what we do actually. We have a pre-cursor to the app that queries the user for which account/region they want to deploy to, and then those are passed into the App. CDK has also introduced `Stage`s which seems to be a rough equivalent of what we're talking about here. – Digital Deception Feb 01 '22 at 00:59
  • Did this answer your question, or was there something else you were after @Laimonas Sutkus? – Digital Deception Feb 01 '22 at 21:41
  • there is a much better way than having two seperate apps - since its code, you can use variables to represent your environments - and context values (`cdk deploy appname -c context_var=Value` ) to define what env you are deploying. - inf as code is best kept dry, otherwise whats the point? – lynkfox Jun 01 '22 at 10:26
  • @lynkfox I'm not quite sure how that differs from what we were saying. We are talking about having two separate apps, each with a different set of infrastructure. We then pass arguments into those apps determining which environment we use. e.g., cdk deploy "node pipeline-app.js --team abc --region us-east-1 --stage test" – Digital Deception Jun 02 '22 at 04:05
  • because you now have duplicate code that is doing the same thing. yes, its just two instantiations at the top level, but due to the nature the app you are needlessly complicating the structure for no perceivable benefit that can't be done with 1 app, 1 stack, and a `-c deploy=PROD` or `-c deploy=DEV`. I can see no advantage of having a second app when a simple flag can accomplish the exact same thing – lynkfox Jun 02 '22 at 17:26
  • It's not doing the same thing? In our particular case, we have one app and one stack for deploying the CI/CD infrastructure. We then have an additional app/stack for deploying the actual service infrastructure. Now we could combine the two apps into a single app, and determine which stack to deploy based on a flag but I'd argue the pro/cons list is not clear cut. Again, we're not talking about an app per environment, we're talking about an app per set of stacks. So your flags would be more appropriately `-c deploy=pipeline` and `-c deploy=service`. – Digital Deception Jun 03 '22 at 04:28
2

Having re-read your question, the short answer is no. In testing this, I found that CDK would only create the second app defined.

You can, however, deploy multiple-stack applications:

https://docs.aws.amazon.com/cdk/latest/guide/stack_how_to_create_multiple_stacks.html

It's also possible to reference resources from one stack in another, by using core.CfnOutput and core.Fn.importValue:

https://docs.aws.amazon.com/cdk/api/latest/python/aws_cdk.core/CfnOutput.html https://docs.aws.amazon.com/cdk/api/latest/python/aws_cdk.core/Fn.html

Under the hood, this uses CloudFormation's ability to export outputs and import them in other stacks. Effectively your multiple stack CDK app will create nested CloudFormation stacks.

In terms of deployments, CDK creates a CloudFormation change set and deploys it, so all changes will be deployed on cdk deploy. From your perspective, it'll be synchronous, but there may be some asynchronous API calls happening under the hood through CloudFormation.

garwil
  • 96
  • 5
1

Based on your comments - mainly seeking to do parrallel deployments from your local - you don't need multiple apps.

Just open a new terminal shell and start deploying again, using stack names to define which stack you are currently deploying:

**Shell 1**
cdk deploy StackName1

> Open a new terminal window
**Shell 2**
cdk deploy OtherStackName

and they will both run simultaneously. They have no interaction with each other, and if they depend on each others resources to be deployed in a certain order this will simply be a recipe for disaster.

but if all you are looking for is speed of deployment, then yeah. This will do the trick just fine.

If this is a common action however, you'd be best advised to set up a CodePipeline with one stage having two CodeDeploy actions to deploy your stacks from the synthed templates (or two codebuilds to do the same thing using cdk deploy)

lynkfox
  • 2,003
  • 1
  • 8
  • 16
0

Yes, you can do pretty much the exact thing that you gave as an example in your question: have 2 apps and synthesize them into 2 separate folders. You do that by overriding outdir prop for each app, otherwise they would override each other's compiled files. See more complete example at the end.

A few caveats though!

  • As of the time of this writing, this is most likely unsupported. In the docs of the outdir property it says:

    You should never need to set this value.

    This property is intended for internal and testing use.

    • So take it or leave it on your own risk :)
  • Calling cdk synth on this project will indeed create 2 folders with the right files but the command fails with ENOENT: no such file or directory, open 'cdk.out/manifest.json'. The mentioned folder cdk.out is created too, it's just empty. So I guess the CDK team doesn't account for anyone using this approach. I don't know CDK internals well enough to be 100% sure but from a brief glance into the compiled templates, the output looks ok and should probably work.

  • You are limited in what you can share between the apps. Note that when you instantiate a stack, the first argument is an app. Therefore, for the second app you need a new instantiation.

You can deploy each app separately with --app flag, e.g. cdk deploy --app cdk.out.dev

Full example here:

#!/usr/bin/env node
import "source-map-support/register";
import * as cdk from "aws-cdk-lib";
import { EventInfrastructureStack } from "../lib/stacks/event-infrastructure-stack";

const devApp = new cdk.App({
  outdir: "cdk.out.dev",
});
new EventInfrastructureStack(devApp, "EventInfrastructureStack", {
  env: {
    account: "account1",
    region: "eu-west-1",
  },
});

const prodApp = new cdk.App({
  outdir: "cdk.out.prod",
});
new EventInfrastructureStack(prodApp, "EventInfrastructureStack", {
  env: {
    account: "acount2",
    region: "eu-west-1",
  },
});

devApp.synth();
prodApp.synth();

Now, you didn't tell us what were you trying to achieve. My goal when first looking into this was to have a separate app for each environment. CDK offers Stage construct for this purpose, docs here.

An abstract application modeling unit consisting of Stacks that should be deployed together.

You can then instantiate (stage) multiple times to model multiple copies of your application which should be be deployed to different environments.

Maybe that's what you were really looking for?

Samuel
  • 2,430
  • 5
  • 31
  • 41