Edit: since this answer has reasonable amount of views, I want to update it to reflect a better, more CDK native approach. I will keep original answer below as it might work better for someone.
CDK parameters can be stored directly in under cdk.json key and retrieved inside the app by using context.ConstructNode.try_get_context(param_name)
See official doc: https://docs.aws.amazon.com/cdk/latest/guide/get_context_var.html
So this is an example of with different params for different envs:cdk.json
{
"app": "python3 app.py",
"context": {
"dev": {
"vpc": {
"vpc_id": "blabla"
}
},
"stage": {
"vpc": {
"vpc_id": "bleble"
}
}
}
Now you can also supply context param via CLI key and use its value in code to get relevant settings.--context env_name=dev
Practically most if not all common use constructs like , Stack, App all have NestedStack attribute, so you can access context from anywhere in your app.node
There are 2 caveats to using it:
It doesn't allow access to lower level keys (or at least it's not documented). This means you cannot use , you need to retrieve top level key by self.try_get_context("dev.vpc.vpc_id") and make your way down on your own.self.try_get_context("dev")
If you have params in your context and try to override them by using CLI key bool, these params will be turned into --context key=False, and it's very easy to fall into trap if you're using common sense syntax of:str
if self.try_get_context("deploy_vpc"):
MyVPC(app, "my new vpc")
Since "False" is evaluated to be as a non-empty string, you will not get what you expect.True
OLD ANSWER
I'm not sure if consensus on this exists, since CDK is still a new thing and also supports multiple languages. But what I've personally done is storing settings in YAML file for different environments, and then supplying that file via environment variable.
Python example:
Config is a YAML file which holds items like required tags for resources, some settings on app level (like account ID and region) and some stack level settings (like names of resources etc).
Assuming standard project layout, where you have main file and app.py file with stack description.
In cdk/cdk_stack.py:app.py
from ruamel.yaml import YAML
...
from cdk.cdk_stack import MyStack
def load_config() -> dict:
"""
Import settings from YAML config
:return: dict with configuration items
"""
# This variable should always be set
# CDK doesn't work well with argparse
config_path = os.getenv("CONFIG_PATH")
if not config_path:
raise RuntimeError("You need to supply config file path with CONFIG_PATH env variable")
# We don't verify config content, let fail in case something is missing
with open(config_path) as config_file:
config = YAML().load(config_file.read())
return config
def init_app() -> core.App:
"""
Initiates CDK main_app for deployment
:return: main_app
"""
main_app = core.App()
config = load_config()
# Account ID and region have to be explicitly set in order to import existing resources
MyStack(
main_app,
"my stack",
env={
'account': config["account_id"],
'region': config["region"]
},
config=config
)
# Tags are applied to all tagable resources in the stack
for key, value in config["tags"].items():
core.Tag.add(scope=main_app, key=key, value=value)
return main_app
if __name__ == '__main__':
app = init_app()
app.synth()
Then in :cdk/cdk_stack.py
class MyStack(core.Stack):
"""
Describes CF resources
"""
def __init__(self, scope: core.Construct, id: str, **kwargs) -> None:
# Pop the config object first, since parent object doesn't expect it and will crash
config = kwargs.pop("config")
super().__init__(scope, id, **kwargs)
...