Get a list of stack dependencies using CDK cli - aws-cdk

Is it somehow possible to get a list of the stacks that another stack depends on using the AWS CDK CLI? For example, given a list of stacks that looks something like:
const app = new App();
const alphaStack = new Stack(app);
const betaStack = new Stack(app);
betaStack.addDependency(alphaStack);
const gammaStack = new Stack(app);
gammaStack.addDependency(gammaStack);
const deltaStack = new Stack(app);
deltaStack.addDependency(betaStack);
deltaStack.addDependency(gammaStack);
I'd like to run a command that could give me output similar to the following:
$ cdk list-deps alpha-stack # no result
$ cdk list-deps beta-stack
alpha-stack
$ cdk list-deps gamma-stack
alpha-stack
$ cdk list-deps delta-stack
beta-stack
gamma-stack
Specifically, I'd like to be able to run this before I deploy my stacks.

Run cdk synth STACK_NAME
and you should find the dependencies from cdk.out/manifest.json
❯ jq '.artifacts.STACK_NAME.dependencies' cdk.out/manifest.json
[
"STACK_NAME.assets"
...
]

In case this helps anyone in the future; the following seems to solve the problem that I wanted to solve:
// the stack that we're interested in finding deps for
const stackName = "...";
// assuming app is as defined in the question
const { stacks } = app.synth();
stacks
.find(({ stackName }) => stackName === searchForStackName)
?.dependencies.forEach((dep) => console.log(dep.id));
Caveats:
It's obviously not built-in to the CLI, and requires exposing app from your CDK definitions.
This does require calling synth on the app. Initially my understanding was that this creates some artifacts within AWS, which I wanted to avoid; but it seems that's not actually the case.
I'm not sure how reliable/stable dep.id is from this snippet/answer. Although it has so far been robust enough for my purposes, dependencies returns a list of CloudArtifact which I'm not certain if it will always represent a Stack.

I know this isn't quite as robust as what you're looking for but here is a useful command to find the stacks that depend on a particular export:
aws cloudformation list-imports --export-name EXPORT_NAME

You can also run
cdk destroy stackName
It will ask you for confirmation of the dependencies necesary to destroy your stack.
This is a dangerous operation and should be done with care because it can destroy your stacks.

Related

What is the correct way to identify production & development environment in AWS CDK?

I am learning AWS Cloud Development Kit (CDK).
As part of this learning, I am trying to understand how I am supposed to correctly handle production and development environment.
I know AWS CDK provides the environment parameter to allow deploying stacks to specific account.
But then, how to have specific options for development versus production stacks ? It does not seem to be provided by default by AWS CDK or am I missing/misunderstanding something ?
A very simple example could be that I want a S3 bucket called my-s3-bucket-dev for my development account and one named my-s3-bucket-prod for my production account. But then how to have e.g. a variable stage correctly handled in AWS CDK ?
I know I can add parameters in the cdk.json file but again, I don't know how to correctly use this file to depend upon the deployed stack i.e. production vs development.
Thanks for the support
Welcome to AWS CDK.
Enjoy the ride. ;)
Actually, there is no semantic (in your case the stage) in an account itself.
This has nothing to do with CDK or Cloud Formation.
You need to take care of this.
You're right, that you could use the CDK context in the cdk.json.
There's no schema enforcement in the context, except for some internally used variables by CDK.
You could define your dev and prod objects within.
There are other ways of defining the context.
Here is an example, what it could look like:
{
"app": "node app",
// usually there's some internal definition for your CDK project
"context": {
"dev": {
"accountId" : "prod_account",
"accountRegion" : "us-east-1",
"name": "dev",
"resourceConfig":
{
// here you could differentiate the config per AWS resource-type
// e.g. dev has lower hardware specs
}
},
"prod": {
"accountId" : "prod_account",
"accountRegion" : "us-east-1",
"name": "prod",
"resourceConfig":
{
// here you could differentiate the config per AWS resource-type
// prod has higher hardware specs or more cluster nodes
}
}
}
}
With this being defined, you need to run your CDK application with the -c flag to specify which configuration object (dev or prod), you want to have.
For instance, you could run it with cdk synth -c stage=prod.
This sets the stage variable in your context and makes it available.
When it was successful, you can re-access the context again and fetch the appropriate config object.
const app = new cdk.App();
const stage = app.node.tryGetContext('stage');
// the following step is only needed, if you have a different config per account
const stageConfig = app.node.tryGetContext(stage );
// ... do some validation and pass the config to the stacks as constructor argument
As I said, the context is one way of doing this.
However, there are drawbacks to it.
It's JSON and no code.
What I prefer is to have TypeScript types per resource configuration (e.g. S3) and wire them all together as a plain object.
The object maps the account/region information and the corresponding resource configurations.

How do I include a custom Lambda Layer into a pipeline stack? (AWS-CDK)

I have two stacks,
Application stack
Pipeline stack
What is the official way of including a custom lambda layer into the pipeline stack so that it relays code location information back to my application stack?
I have followed the documentation to make regular lambdas work... found here:
https://docs.aws.amazon.com/cdk/latest/guide/codepipeline_example.html?shortFooter=true
Some documentation and/or example code would be greatly helpful.
Can anyone point me in the right direction?
Thx
Sucks that no one has answered this question until now but I was trying to solve the same problem and came up with this, so I hope it helps someone like us :)
There are zillions of ways to skin this cat but I am trying to go for clean CICD with easy developer work. The route I chose was to build my Lambda Layer with a Code.from_docker_build() object. I supplied a Dockerfile I wrote, which can package my code into whatever code artifact I need and then CDK knows how to handle it. That becomes my Lambda Layer, which I can then consume in other stacks/lambdas.
So here's what you need to do:
Create a Dockerfile in your repo which can build your code into an artifact.
This Dockerfile should finish by putting your single code artifact file into the /asset directory. You should only have a tar ball or a zip or whatever, just 1 file that is your code in "artifact" form that can run in lambda
use Code.from_docker_build() as your code object in your function.
class YourLambdaLayer(cdk.Stack):
def __init__(self, scope: cdk.Construct, construct_id: str, **kwargs) -> None:
super().__init__(scope, construct_id, **kwargs)
# Create Lambda Layer
new_version = LayerVersion(self, construct_id,
layer_version_name=construct_id,
code=Code.from_docker_build(
path=os.path.abspath("./"),
file='Dockerfile'
),
compatible_runtimes=[Runtime.PYTHON_3_8],
)
# Create an export to use from other stacks
cdk.CfnOutput(self, f"{construct_id}-Arn-Export",
value=new_version.layer_version_arn,
export_name='your-cool-layer-Arn'
)
Everything else from the example in the link you posted should work as you expect it. The only difference is that you're using a docker image to bundle your artifacts instead of supplying a zip or something like that.
This is what a pipeline resource would look like for a Python function, built using a Dockerfile in the pipeline...
class YourCICDStack(cdk.Stack):
def __init__(self, scope, id, env=None,... **kwargs):
super().__init__(scope, id,..., env=env, **kwargs)
code_repo = Repository(self, 'a-git-repo-in-code-commit',
repository_name='a-cool-python-package-from-u',
description='you can be long winded whilst describing, if you like :)'
)
pipeline = CodePipeline(self, 'resource-name-goes-here',
pipeline_name='pipeline-name-goes-here',
docker_enabled_for_synth=True, # !!! important !!!
synth=ShellStep("Synth",
input=CodePipelineSource.code_commit(
repository=code_repo,
branch='development',
),
commands=[
"pip install -r requirements.txt",
"npm install -g aws-cdk",
f"cdk synth ..."
]
)
)
# Add the stages for deploying
your_stage = pipeline.add_stage(YourLayerStage(self, ..., env=env))
your_stage.add_post(ManualApprovalStep('approval'))
So now that you've got your pipeline publishing your Lambda Layer, you would use it from other stacks using either the from_layer_version_arn() or from_layer_version_attributes().
Those are both class functions, so you use them in your other stacks by doing something like
my_cool_layer_ref = LayerVersion.from_layer_version_arn(
cdk.Fn.import_value('your-cool-layer-Arn')
)
# Include it in your other stacks/functions
some_other_func = Function(...,
layers=[my_cool_layer_ref],
...
)

How to automatically manage and update CDK version with Projen?

Context:
Projen is an awesome tool to generate and manage (JSII-built) AWS CDK projects.
Background:
Previously I have managed CDK dependencies with RenovateBot's group:aws-cdkMonorepo preset. This will result in RenovateBot creating a single Github Pull Request for AWS CDK depedency updates.
Question:
With Projen, one controls the CDK version in .projenrc.js:
const { AwsCdkConstructLibrary } = require('projen');
const project = new AwsCdkConstructLibrary({
authorName: "Example",
authorAddress: "contact#example.com",
cdkVersion: "1.64.0",
name: "#example/project",
repository: "https://github.com/example/project.git",
});
project.synth();
So how can one manage that cdkVersion value with tooling such as DependaBot or RenovateBot?
Since keeping one's CDK constructs up-to-date with current CDK version is critial and with multiple CDK constructs doing it by hand will be painful.
The central version management depends on your requirement. If you are using a centralized construct library it is a must-have.
For managing the dependencies in centrally in a single configuration, you need to add the following snippet in the .projenrc.js
cdkDependecies:[
'#aws-cdk/core'
]
Now, whenever you run projen the cdk app would be managed centrally and use the latest version.

How do I connect xterm.js(in electron) to a real working command prompt?

I've dug myself into a deep rabbit hole trying to find out what the title says. If you're confused about what this question is, I'll give a more detailed explanation: Have you ever seen the VSCode Terminal? or Terminus? Well I want to do what those applications do. I have an electron app, and for the users convenience I want to include a command prompt of some sorts into it. I've looked in to xterm.js, but I can only find examples of things like SSH, not a direct link to a console hosted on the system. What I'm asking is how do I connect xterm.js(in electron) to a real working command prompt? I've seen programs able to interact with cmd.exe such as Windows Terminal. I'll use it as an example.
Image taken from process hacker
In the attached photo you can see three processes. WindowsTerminal.exe, OpenConsole.exe, and cmd.exe. After digging around in the source code of Windows Terminal, I can see the OpenConsole.exe gets started with every instance of a cmd that you make. So I assume that is the program that's interacting with cmd.exe. Now I know that Windows Terminal is made using UWP but you can see similar things happening with VSCode(I opened a bunch of terminals to demonstrate)
here is another post with a similar question, but with no answers. I hope this one gains some traction.
So if you can answer, thanks. If you got sidetracked a bit, remember my question: How do I connect xterm.js(in electron) to a real working command prompt?
Sorry if you couldn't understand my wording, im not very good at this :P
The following video was helpful for me. Shortly, you need to :
install node-pty and electron-rebuild packages (additional to the xterm)
Place the following codes to appropriate process files
In the main process (main.js):
const os = require('os');
const pty = require('node-pty');
var shell = os.platform() === "win32" ? "powershell.exe" : "bash";
var ptyProcess = pty.spawn(shell, [], {
name: 'xterm-color',
cols: 80,
rows: 24,
cwd: process.env.HOME,
env: process.env
});
ptyProcess.on("data", (data) => {
mainWindow.webContents.send("terminal-incData", data);
});
ipcMain.on("terminal-into", (event, data)=> {
ptyProcess.write(data);
})
In the renderer process:
const Terminal = require('xterm').Terminal;
const FitAddon = require('xterm-addon-fit').FitAddon;
const term = new Terminal();
const fitAddon = new FitAddon();
term.loadAddon(fitAddon);
// Open the terminal in #terminal-container
term.open(document.getElementById('terminal-container'));
term.onData(e => {
ipcRenderer.send("terminal-into", e);
} );
ipcRenderer.on('terminal-incData', (event, data) => {
term.write(data);
})
// Make the terminal's size and geometry fit the size of #terminal-container
fitAddon.fit();
run electron-rebuild command if you get an error.
You might get the following errors when you try to install electron-rebuild package:
Solution for MAC: error: no template named 'remove_cv_t'.
Solution for Windows: gyp ERR! find Python
I figured it out, code on github: https://github.com/77Z/electron-local-terminal-prototype

Equivalence of Rails console for Node.js

I am trying out Node.js Express framework, and looking for plugin that allows me to interact with my models via console, similar to Rails console. Is there such a thing in NodeJS world?
If not, how can I interact with my Node.js models and data, such as manually add/remove objects, test methods on data etc.?
Create your own REPL by making a js file (ie: console.js) with the following lines/components:
Require node's built-in repl: var repl = require("repl");
Load in all your key variables like db, any libraries you swear by, etc.
Load the repl by using var replServer = repl.start({});
Attach the repl to your key variables with replServer.context.<your_variable_names_here> = <your_variable_names_here>. This makes the variable available/usable in the REPL (node console).
For example: If you have the following line in your node app:
var db = require('./models/db')
Add the following lines to your console.js
var db = require('./models/db');
replServer.context.db = db;
Run your console with the command node console.js
Your console.js file should look something like this:
var repl = require("repl");
var epa = require("epa");
var db = require("db");
// connect to database
db.connect(epa.mongo, function(err){
if (err){ throw err; }
// open the repl session
var replServer = repl.start({});
// attach modules to the repl context
replServer.context.epa = epa;
replServer.context.db = db;
});
You can even customize your prompt like this:
var replServer = repl.start({
prompt: "Node Console > ",
});
For the full setup and more details, check out:
http://derickbailey.com/2014/07/02/build-your-own-app-specific-repl-for-your-nodejs-app/
For the full list of options you can pass the repl like prompt, color, etc: https://nodejs.org/api/repl.html#repl_repl_start_options
Thank you to Derick Bailey for this info.
UPDATE:
GavinBelson has a great recommendation for running with sequelize ORM (or anything that requires promise handling in the repl).
I am now running sequelize as well, and for my node console I'm adding the --experimental-repl-await flag.
It's a lot to type in every time, so I highly suggest adding:
"console": "node --experimental-repl-await ./console.js"
to the scripts section in your package.json so you can just run:
npm run console
and not have to type the whole thing out.
Then you can handle promises without getting errors, like this:
const product = await Product.findOne({ where: { id: 1 });
I am not very experienced in using node, but you can enter node in the command line to get to the node console. I then used to require the models manually
Here is the way to do it, with SQL databases:
Install and use Sequelize, it is Node's ORM answer to Active Record in Rails. It even has a CLI for scaffolding models and migrations.
node --experimental-repl-await
> models = require('./models');
> User = models.User; //however you load the model in your actual app this may vary
> await User.findAll(); //use await, then any sequelize calls here
TLDR
This gives you access to all of the models just as you would in Rails active record. Sequelize takes a bit of getting used to, but in many ways it is actually more flexible than Active Record while still having the same features.
Sequelize uses promises, so to run these properly in REPL you will want to use the --experimental-repl-await flag when running node. Otherwise, you can get bluebird promise errors
If you don't want to type out the require('./models') step, you can use console.js - a setup file for REPL at the root of your directory - to preload this. However, I find it easier to just type this one line out in REPL
It's simple: add a REPL to your program
This may not fully answer your question, but to clarify, node.js is much lower-level than Rails, and as such doesn't prescribe tools and data models like Rails. It's more of a platform than a framework.
If you are looking for a more Rails-like experience, you may want to look at a more 'full-featured' framework built on top of node.js, such as Meteor, etc.

Resources