cdk-remote-stack


Namecdk-remote-stack JSON
Version 2.0.101 PyPI version JSON
download
home_pagehttps://github.com/pahud/cdk-remote-stack.git
SummaryGet outputs and AWS SSM parameters from cross-region AWS CloudFormation stacks
upload_time2023-10-19 00:22:00
maintainer
docs_urlNone
authorPahud Hsieh<pahudnet@gmail.com>
requires_python~=3.7
licenseApache-2.0
keywords
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            [![npm version](https://badge.fury.io/js/cdk-remote-stack.svg)](https://badge.fury.io/js/cdk-remote-stack)
[![PyPI version](https://badge.fury.io/py/cdk-remote-stack.svg)](https://badge.fury.io/py/cdk-remote-stack)
[![release](https://github.com/pahud/cdk-remote-stack/actions/workflows/release.yml/badge.svg)](https://github.com/pahud/cdk-remote-stack/actions/workflows/release.yml)

# cdk-remote-stack

Get outputs and AWS SSM parameters from cross-region AWS CloudFormation stacks

# Install

Use the npm dist tag to opt in CDKv1 or CDKv2:

```sh
// for CDKv2
npm install cdk-remote-stack
or
npm install cdk-remote-stack@latest

// for CDKv1
npm install cdk-remote-stack@cdkv1
```

# Why

Setting up cross-regional cross-stack references requires using multiple constructs from the AWS CDK construct library and is not straightforward.

`cdk-remote-stack` aims to simplify the cross-regional cross-stack references to help you easily build cross-regional multi-stack AWS CDK applications.

This construct library provides two main constructs:

* **RemoteOutputs** - cross regional stack outputs reference.
* **RemoteParameters** - cross regional/account SSM parameters reference.

# RemoteOutputs

`RemoteOutputs` is ideal for one stack referencing the outputs from another across different AWS regions.

Let's say we have two cross-regional stacks in the same AWS CDK application:

1. **stackJP** - stack in Japan (`JP`) to create a SNS topic
2. **stackUS** - stack in United States (`US`) to get the outputs from `stackJP` and print out the SNS `TopicName` from `stackJP` outputs.

```python
import { RemoteOutputs } from 'cdk-remote-stack';
import * as cdk from 'aws-cdk-lib';

const app = new cdk.App();

const envJP = {
  region: 'ap-northeast-1',
  account: process.env.CDK_DEFAULT_ACCOUNT,
};

const envUS = {
  region: 'us-west-2',
  account: process.env.CDK_DEFAULT_ACCOUNT,
};

// first stack in JP
const stackJP = new cdk.Stack(app, 'demo-stack-jp', { env: envJP })

new cdk.CfnOutput(stackJP, 'TopicName', { value: 'foo' })

// second stack in US
const stackUS = new cdk.Stack(app, 'demo-stack-us', { env: envUS })

// ensure the dependency
stackUS.addDependency(stackJP)

// get the stackJP stack outputs from stackUS
const outputs = new RemoteOutputs(stackUS, 'Outputs', { stack: stackJP })

const remoteOutputValue = outputs.get('TopicName')

// the value should be exactly the same with the output value of `TopicName`
new cdk.CfnOutput(stackUS, 'RemoteTopicName', { value: remoteOutputValue })
```

At this moment, `RemoteOutputs` only supports cross-regional reference in a single AWS account.

## Always get the latest stack output

By default, the `RemoteOutputs` construct will always try to get the latest output from the source stack. You may opt out by setting `alwaysUpdate` to `false` to turn this feature off.

For example:

```python
const outputs = new RemoteOutputs(stackUS, 'Outputs', {
  stack: stackJP,
  alwaysUpdate: false,
})
```

# RemoteParameters

[AWS Systems Manager (AWS SSM) Parameter Store](https://docs.aws.amazon.com/systems-manager/latest/userguide/systems-manager-parameter-store.html) is great to store and persist parameters and allow stacks from other regons/accounts to reference. Let's dive into the two major scenarios below:

## Stacks from single account and different regions

In this sample, we create two stacks from JP (`ap-northeast-1`) and US (`us-west-2`). The JP stack will produce and update parameters in its parameter store, while the US stack will consume the parameters across differnt regions with the `RemoteParameters` construct.

![](images/remote-param-1.svg)

```python
    const envJP = { region: 'ap-northeast-1', account: '111111111111' };
    const envUS = { region: 'us-west-2', account: '111111111111' };

    // first stack in JP
    const producerStackName = 'demo-stack-jp';
    const stackJP = new cdk.Stack(app, producerStackName, { env: envJP });
    const parameterPath = `/${envJP.account}/${envJP.region}/${producerStackName}`

    new ssm.StringParameter(stackJP, 'foo1', {
      parameterName: `${parameterPath}/foo1`,
      stringValue: 'bar1',
    });
    new ssm.StringParameter(stackJP, 'foo2', {
      parameterName: `${parameterPath}/foo2`,
      stringValue: 'bar2',
    });
    new ssm.StringParameter(stackJP, 'foo3', {
      parameterName: `${parameterPath}/foo3`,
      stringValue: 'bar3',
    });

    // second stack in US
    const stackUS = new cdk.Stack(app, 'demo-stack-us', { env: envUS });

    // ensure the dependency
    stackUS.addDependency(stackJP);

    // get remote parameters by path from AWS SSM parameter store
    const parameters = new RemoteParameters(stackUS, 'Parameters', {
      path: parameterPath,
      region: stackJP.region,
    })

    const foo1 = parameters.get(`${parameterPath}/foo1`);
    const foo2 = parameters.get(`${parameterPath}/foo2`);
    const foo3 = parameters.get(`${parameterPath}/foo3`);

    new cdk.CfnOutput(stackUS, 'foo1Output', { value: foo1 });
    new cdk.CfnOutput(stackUS, 'foo2Output', { value: foo2 });
    new cdk.CfnOutput(stackUS, 'foo3Output', { value: foo3 });
```

## Stacks from differnt accounts and different regions

Similar to the use case above, but now we deploy stacks in separate accounts and regions.  We will need to pass an AWS Identity and Access Management (AWS IAM) `role` to the `RemoteParameters` construct to get all the parameters from the remote environment.

![](images/remote-param-2.svg)

```python

    const envJP = { region: 'ap-northeast-1', account: '111111111111' };
    const envUS = { region: 'us-west-2', account: '222222222222' };

    // first stack in JP
    const producerStackName = 'demo-stack-jp';
    const stackJP = new cdk.Stack(app, producerStackName, { env: envJP });
    const parameterPath = `/${envJP.account}/${envJP.region}/${producerStackName}`

    new ssm.StringParameter(stackJP, 'foo1', {
      parameterName: `${parameterPath}/foo1`,
      stringValue: 'bar1',
    });
    new ssm.StringParameter(stackJP, 'foo2', {
      parameterName: `${parameterPath}/foo2`,
      stringValue: 'bar2',
    });
    new ssm.StringParameter(stackJP, 'foo3', {
      parameterName: `${parameterPath}/foo3`,
      stringValue: 'bar3',
    });

    // allow US account to assume this read only role to get parameters
    const cdkReadOnlyRole = new iam.Role(stackJP, 'readOnlyRole', {
      assumedBy: new iam.AccountPrincipal(envUS.account),
      roleName: PhysicalName.GENERATE_IF_NEEDED,
      managedPolicies: [ iam.ManagedPolicy.fromAwsManagedPolicyName('AmazonSSMReadOnlyAccess')],
    })

    // second stack in US
    const stackUS = new cdk.Stack(app, 'demo-stack-us', { env: envUS });

    // ensure the dependency
    stackUS.addDependency(stackJP);

    // get remote parameters by path from AWS SSM parameter store
    const parameters = new RemoteParameters(stackUS, 'Parameters', {
      path: parameterPath,
      region: stackJP.region,
      // assume this role for cross-account parameters
      role: iam.Role.fromRoleArn(stackUS, 'readOnlyRole', cdkReadOnlyRole.roleArn),
    })

    const foo1 = parameters.get(`${parameterPath}/foo1`);
    const foo2 = parameters.get(`${parameterPath}/foo2`);
    const foo3 = parameters.get(`${parameterPath}/foo3`);

    new cdk.CfnOutput(stackUS, 'foo1Output', { value: foo1 });
    new cdk.CfnOutput(stackUS, 'foo2Output', { value: foo2 });
    new cdk.CfnOutput(stackUS, 'foo3Output', { value: foo3 });
```

## Dedicated account for a centralized parameter store

The parameters are stored in a centralized account/region and previously provisioned as a source-of-truth configuration store. All other stacks from different accounts/regions are consuming the parameters from the central configuration store.

This scenario is pretty much like #2. The difference is that there's a dedicated account for centralized configuration store being shared with all other accounts.

![](images/remote-param-3.svg)

You will need create `RemoteParameters` for all the consuming stacks like:

```python
// for StackUS
new RemoteParameters(stackUS, 'Parameters', {
  path: parameterPath,
  region: 'eu-central-1'
  // assume this role for cross-account parameters
  role: iam.Role.fromRoleArn(stackUS, 'readOnlyRole', sharedReadOnlyRoleArn),
})

// for StackJP
new RemoteParameters(stackJP, 'Parameters', {
  path: parameterPath,
  region: 'eu-central-1'
  // assume this role for cross-account parameters
  role: iam.Role.fromRoleArn(stackJP, 'readOnlyRole', sharedReadOnlyRoleArn),
})
```

## Tools for multi-account deployment

You will need to install and bootstrap your target accounts with AWS CDK 1.108.0 or later, so you can deploy stacks from different accounts. It [adds support](https://github.com/aws/aws-cdk/pull/14874) for cross-account lookups. Alternatively, install [cdk-assume-role-credential-plugin](https://github.com/aws-samples/cdk-assume-role-credential-plugin). Read this [blog post](https://aws.amazon.com/tw/blogs/devops/cdk-credential-plugin/) to setup this plugin.

## Limitations

1. At this moment, the `RemoteParameters` construct only supports the `String` data type from parameter store.
2. Maximum number of parameters is `100`. Will make it configurable in the future if required.

# Contributing

See [CONTRIBUTING](CONTRIBUTING.md) for more information.

# License

This code is licensed under the Apache License 2.0. See the [LICENSE](LICENSE) file.

            

Raw data

            {
    "_id": null,
    "home_page": "https://github.com/pahud/cdk-remote-stack.git",
    "name": "cdk-remote-stack",
    "maintainer": "",
    "docs_url": null,
    "requires_python": "~=3.7",
    "maintainer_email": "",
    "keywords": "",
    "author": "Pahud Hsieh<pahudnet@gmail.com>",
    "author_email": "",
    "download_url": "https://files.pythonhosted.org/packages/8e/25/fd82fb7af24ad2045df584a39bad3202f80d58499faadddf47d0de0e9186/cdk-remote-stack-2.0.101.tar.gz",
    "platform": null,
    "description": "[![npm version](https://badge.fury.io/js/cdk-remote-stack.svg)](https://badge.fury.io/js/cdk-remote-stack)\n[![PyPI version](https://badge.fury.io/py/cdk-remote-stack.svg)](https://badge.fury.io/py/cdk-remote-stack)\n[![release](https://github.com/pahud/cdk-remote-stack/actions/workflows/release.yml/badge.svg)](https://github.com/pahud/cdk-remote-stack/actions/workflows/release.yml)\n\n# cdk-remote-stack\n\nGet outputs and AWS SSM parameters from cross-region AWS CloudFormation stacks\n\n# Install\n\nUse the npm dist tag to opt in CDKv1 or CDKv2:\n\n```sh\n// for CDKv2\nnpm install cdk-remote-stack\nor\nnpm install cdk-remote-stack@latest\n\n// for CDKv1\nnpm install cdk-remote-stack@cdkv1\n```\n\n# Why\n\nSetting up cross-regional cross-stack references requires using multiple constructs from the AWS CDK construct library and is not straightforward.\n\n`cdk-remote-stack` aims to simplify the cross-regional cross-stack references to help you easily build cross-regional multi-stack AWS CDK applications.\n\nThis construct library provides two main constructs:\n\n* **RemoteOutputs** - cross regional stack outputs reference.\n* **RemoteParameters** - cross regional/account SSM parameters reference.\n\n# RemoteOutputs\n\n`RemoteOutputs` is ideal for one stack referencing the outputs from another across different AWS regions.\n\nLet's say we have two cross-regional stacks in the same AWS CDK application:\n\n1. **stackJP** - stack in Japan (`JP`) to create a SNS topic\n2. **stackUS** - stack in United States (`US`) to get the outputs from `stackJP` and print out the SNS `TopicName` from `stackJP` outputs.\n\n```python\nimport { RemoteOutputs } from 'cdk-remote-stack';\nimport * as cdk from 'aws-cdk-lib';\n\nconst app = new cdk.App();\n\nconst envJP = {\n  region: 'ap-northeast-1',\n  account: process.env.CDK_DEFAULT_ACCOUNT,\n};\n\nconst envUS = {\n  region: 'us-west-2',\n  account: process.env.CDK_DEFAULT_ACCOUNT,\n};\n\n// first stack in JP\nconst stackJP = new cdk.Stack(app, 'demo-stack-jp', { env: envJP })\n\nnew cdk.CfnOutput(stackJP, 'TopicName', { value: 'foo' })\n\n// second stack in US\nconst stackUS = new cdk.Stack(app, 'demo-stack-us', { env: envUS })\n\n// ensure the dependency\nstackUS.addDependency(stackJP)\n\n// get the stackJP stack outputs from stackUS\nconst outputs = new RemoteOutputs(stackUS, 'Outputs', { stack: stackJP })\n\nconst remoteOutputValue = outputs.get('TopicName')\n\n// the value should be exactly the same with the output value of `TopicName`\nnew cdk.CfnOutput(stackUS, 'RemoteTopicName', { value: remoteOutputValue })\n```\n\nAt this moment, `RemoteOutputs` only supports cross-regional reference in a single AWS account.\n\n## Always get the latest stack output\n\nBy default, the `RemoteOutputs` construct will always try to get the latest output from the source stack. You may opt out by setting `alwaysUpdate` to `false` to turn this feature off.\n\nFor example:\n\n```python\nconst outputs = new RemoteOutputs(stackUS, 'Outputs', {\n  stack: stackJP,\n  alwaysUpdate: false,\n})\n```\n\n# RemoteParameters\n\n[AWS Systems Manager (AWS SSM) Parameter Store](https://docs.aws.amazon.com/systems-manager/latest/userguide/systems-manager-parameter-store.html) is great to store and persist parameters and allow stacks from other regons/accounts to reference. Let's dive into the two major scenarios below:\n\n## Stacks from single account and different regions\n\nIn this sample, we create two stacks from JP (`ap-northeast-1`) and US (`us-west-2`). The JP stack will produce and update parameters in its parameter store, while the US stack will consume the parameters across differnt regions with the `RemoteParameters` construct.\n\n![](images/remote-param-1.svg)\n\n```python\n    const envJP = { region: 'ap-northeast-1', account: '111111111111' };\n    const envUS = { region: 'us-west-2', account: '111111111111' };\n\n    // first stack in JP\n    const producerStackName = 'demo-stack-jp';\n    const stackJP = new cdk.Stack(app, producerStackName, { env: envJP });\n    const parameterPath = `/${envJP.account}/${envJP.region}/${producerStackName}`\n\n    new ssm.StringParameter(stackJP, 'foo1', {\n      parameterName: `${parameterPath}/foo1`,\n      stringValue: 'bar1',\n    });\n    new ssm.StringParameter(stackJP, 'foo2', {\n      parameterName: `${parameterPath}/foo2`,\n      stringValue: 'bar2',\n    });\n    new ssm.StringParameter(stackJP, 'foo3', {\n      parameterName: `${parameterPath}/foo3`,\n      stringValue: 'bar3',\n    });\n\n    // second stack in US\n    const stackUS = new cdk.Stack(app, 'demo-stack-us', { env: envUS });\n\n    // ensure the dependency\n    stackUS.addDependency(stackJP);\n\n    // get remote parameters by path from AWS SSM parameter store\n    const parameters = new RemoteParameters(stackUS, 'Parameters', {\n      path: parameterPath,\n      region: stackJP.region,\n    })\n\n    const foo1 = parameters.get(`${parameterPath}/foo1`);\n    const foo2 = parameters.get(`${parameterPath}/foo2`);\n    const foo3 = parameters.get(`${parameterPath}/foo3`);\n\n    new cdk.CfnOutput(stackUS, 'foo1Output', { value: foo1 });\n    new cdk.CfnOutput(stackUS, 'foo2Output', { value: foo2 });\n    new cdk.CfnOutput(stackUS, 'foo3Output', { value: foo3 });\n```\n\n## Stacks from differnt accounts and different regions\n\nSimilar to the use case above, but now we deploy stacks in separate accounts and regions.  We will need to pass an AWS Identity and Access Management (AWS IAM) `role` to the `RemoteParameters` construct to get all the parameters from the remote environment.\n\n![](images/remote-param-2.svg)\n\n```python\n\n    const envJP = { region: 'ap-northeast-1', account: '111111111111' };\n    const envUS = { region: 'us-west-2', account: '222222222222' };\n\n    // first stack in JP\n    const producerStackName = 'demo-stack-jp';\n    const stackJP = new cdk.Stack(app, producerStackName, { env: envJP });\n    const parameterPath = `/${envJP.account}/${envJP.region}/${producerStackName}`\n\n    new ssm.StringParameter(stackJP, 'foo1', {\n      parameterName: `${parameterPath}/foo1`,\n      stringValue: 'bar1',\n    });\n    new ssm.StringParameter(stackJP, 'foo2', {\n      parameterName: `${parameterPath}/foo2`,\n      stringValue: 'bar2',\n    });\n    new ssm.StringParameter(stackJP, 'foo3', {\n      parameterName: `${parameterPath}/foo3`,\n      stringValue: 'bar3',\n    });\n\n    // allow US account to assume this read only role to get parameters\n    const cdkReadOnlyRole = new iam.Role(stackJP, 'readOnlyRole', {\n      assumedBy: new iam.AccountPrincipal(envUS.account),\n      roleName: PhysicalName.GENERATE_IF_NEEDED,\n      managedPolicies: [ iam.ManagedPolicy.fromAwsManagedPolicyName('AmazonSSMReadOnlyAccess')],\n    })\n\n    // second stack in US\n    const stackUS = new cdk.Stack(app, 'demo-stack-us', { env: envUS });\n\n    // ensure the dependency\n    stackUS.addDependency(stackJP);\n\n    // get remote parameters by path from AWS SSM parameter store\n    const parameters = new RemoteParameters(stackUS, 'Parameters', {\n      path: parameterPath,\n      region: stackJP.region,\n      // assume this role for cross-account parameters\n      role: iam.Role.fromRoleArn(stackUS, 'readOnlyRole', cdkReadOnlyRole.roleArn),\n    })\n\n    const foo1 = parameters.get(`${parameterPath}/foo1`);\n    const foo2 = parameters.get(`${parameterPath}/foo2`);\n    const foo3 = parameters.get(`${parameterPath}/foo3`);\n\n    new cdk.CfnOutput(stackUS, 'foo1Output', { value: foo1 });\n    new cdk.CfnOutput(stackUS, 'foo2Output', { value: foo2 });\n    new cdk.CfnOutput(stackUS, 'foo3Output', { value: foo3 });\n```\n\n## Dedicated account for a centralized parameter store\n\nThe parameters are stored in a centralized account/region and previously provisioned as a source-of-truth configuration store. All other stacks from different accounts/regions are consuming the parameters from the central configuration store.\n\nThis scenario is pretty much like #2. The difference is that there's a dedicated account for centralized configuration store being shared with all other accounts.\n\n![](images/remote-param-3.svg)\n\nYou will need create `RemoteParameters` for all the consuming stacks like:\n\n```python\n// for StackUS\nnew RemoteParameters(stackUS, 'Parameters', {\n  path: parameterPath,\n  region: 'eu-central-1'\n  // assume this role for cross-account parameters\n  role: iam.Role.fromRoleArn(stackUS, 'readOnlyRole', sharedReadOnlyRoleArn),\n})\n\n// for StackJP\nnew RemoteParameters(stackJP, 'Parameters', {\n  path: parameterPath,\n  region: 'eu-central-1'\n  // assume this role for cross-account parameters\n  role: iam.Role.fromRoleArn(stackJP, 'readOnlyRole', sharedReadOnlyRoleArn),\n})\n```\n\n## Tools for multi-account deployment\n\nYou will need to install and bootstrap your target accounts with AWS CDK 1.108.0 or later, so you can deploy stacks from different accounts. It [adds support](https://github.com/aws/aws-cdk/pull/14874) for cross-account lookups. Alternatively, install [cdk-assume-role-credential-plugin](https://github.com/aws-samples/cdk-assume-role-credential-plugin). Read this [blog post](https://aws.amazon.com/tw/blogs/devops/cdk-credential-plugin/) to setup this plugin.\n\n## Limitations\n\n1. At this moment, the `RemoteParameters` construct only supports the `String` data type from parameter store.\n2. Maximum number of parameters is `100`. Will make it configurable in the future if required.\n\n# Contributing\n\nSee [CONTRIBUTING](CONTRIBUTING.md) for more information.\n\n# License\n\nThis code is licensed under the Apache License 2.0. See the [LICENSE](LICENSE) file.\n",
    "bugtrack_url": null,
    "license": "Apache-2.0",
    "summary": "Get outputs and AWS SSM parameters from cross-region AWS CloudFormation stacks",
    "version": "2.0.101",
    "project_urls": {
        "Homepage": "https://github.com/pahud/cdk-remote-stack.git",
        "Source": "https://github.com/pahud/cdk-remote-stack.git"
    },
    "split_keywords": [],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "000c94e184e82bd3cb704c0efc5c390b7350b3e042c9c205eebebeb15c3e45e0",
                "md5": "86ea3dc0ee1b5f7379dbd03c2e514b63",
                "sha256": "5577af4f854a628a57e448b1b77c1eac55de55bc5e8d1c135a8781046bddd5ab"
            },
            "downloads": -1,
            "filename": "cdk_remote_stack-2.0.101-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "86ea3dc0ee1b5f7379dbd03c2e514b63",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": "~=3.7",
            "size": 58148,
            "upload_time": "2023-10-19T00:21:58",
            "upload_time_iso_8601": "2023-10-19T00:21:58.669227Z",
            "url": "https://files.pythonhosted.org/packages/00/0c/94e184e82bd3cb704c0efc5c390b7350b3e042c9c205eebebeb15c3e45e0/cdk_remote_stack-2.0.101-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "8e25fd82fb7af24ad2045df584a39bad3202f80d58499faadddf47d0de0e9186",
                "md5": "6834c1927d6392ef1926821714379add",
                "sha256": "0a43ec97e02bd2a021993fd8c9c48b2f29dfbe26e214649d099455cde0e38c89"
            },
            "downloads": -1,
            "filename": "cdk-remote-stack-2.0.101.tar.gz",
            "has_sig": false,
            "md5_digest": "6834c1927d6392ef1926821714379add",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": "~=3.7",
            "size": 59882,
            "upload_time": "2023-10-19T00:22:00",
            "upload_time_iso_8601": "2023-10-19T00:22:00.736889Z",
            "url": "https://files.pythonhosted.org/packages/8e/25/fd82fb7af24ad2045df584a39bad3202f80d58499faadddf47d0de0e9186/cdk-remote-stack-2.0.101.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2023-10-19 00:22:00",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "pahud",
    "github_project": "cdk-remote-stack",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": true,
    "lcname": "cdk-remote-stack"
}
        
Elapsed time: 0.13113s