Skip to main content

cdk-stacksets

Project description

CDK StackSets Construct Library

---

cdk-constructs: Experimental

The APIs of higher level constructs in this module are experimental and under active development. They are subject to non-backward compatible changes or removal in any future version. These are not subject to the Semantic Versioning model and breaking changes will be announced in the release notes. This means that while you may use them, you may need to update your source code when upgrading to a newer version of this package.


This construct library allows you to define AWS CloudFormation StackSets.

stack = Stack()
stack_set_stack = StackSetStack(stack, "MyStackSet")

StackSet(stack, "StackSet",
    target=StackSetTarget.from_accounts(
        regions=["us-east-1"],
        accounts=["11111111111"],
        parameter_overrides={
            "SomeParam": "overrideValue"
        }
    ),
    template=StackSetTemplate.from_stack_set_stack(stack_set_stack)
)

Installing

TypeScript/JavaScript

npm install cdk-stacksets

Python

pip install cdk-stacksets

Java

// add this to your pom.xml
<dependency>
    <groupId>io.github.cdklabs</groupId>
    <artifactId>cdk-stacksets</artifactId>
    <version>0.0.0</version> // replace with version
</dependency>

.NET

dotnet add package CdklabsCdkStacksets --version X.X.X

Go

go get cdk-stacksets-go

Creating a StackSet Stack

StackSets allow you to deploy a single CloudFormation template across multiple AWS accounts and regions. Typically when creating a CDK Stack that will be deployed across multiple environments, the CDK will synthesize separate Stack templates for each environment (account/region combination). Because of the way that StackSets work, StackSet Stacks behave differently. For Stacks that will be deployed via StackSets a single Stack is defined and synthesized. Any environmental differences must be encoded using Parameters.

A special class was created to handle the uniqueness of the StackSet Stack. You declare a StackSetStack the same way that you declare a normal Stack, but there are a couple of differences. StackSetStacks have a couple of special requirements/limitations when compared to Stacks.

Requirements

  • Must be created in the scope of a Stack
  • Must be environment agnostic

Limitations

  • Do not support assets

Once you create a StackSetStack you can create resources within the stack.

stack = Stack()
stack_set_stack = StackSetStack(stack, "StackSet")

iam.Role(stack_set_stack, "MyRole",
    assumed_by=iam.ServicePrincipal("myservice.amazonaws.com")
)

Or

class MyStackSet(StackSetStack):
    def __init__(self, scope, id):
        super().__init__(scope, id)

        iam.Role(self, "MyRole",
            assumed_by=iam.ServicePrincipal("myservice.amazonaws.com")
        )

Creating a StackSet

AWS CloudFormation StackSets enable you to create, update, or delete stacks across multiple accounts and AWS Regions with a single operation. Using an administrator account, you define and manage an AWS CloudFormation template, and use the template as the basis for provisioning stacks into selected target accounts across specific AWS Regions.

There are two methods for defining where the StackSet should be deployed. You can either define individual accounts, or you can define AWS Organizations organizational units.

Deploying to individual accounts

Deploying to individual accounts requires you to specify the account ids. If you want to later deploy to additional accounts, or remove the stackset from accounts, this has to be done by adding/removing the account id from the list.

stack = Stack()
stack_set_stack = StackSetStack(stack, "MyStackSet")

StackSet(stack, "StackSet",
    target=StackSetTarget.from_accounts(
        regions=["us-east-1"],
        accounts=["11111111111"]
    ),
    template=StackSetTemplate.from_stack_set_stack(stack_set_stack)
)

Deploying to organizational units

AWS Organizations is an AWS service that enables you to centrally manage and govern multiple accounts. AWS Organizations allows you to define organizational units (OUs) which are logical groupings of AWS accounts. OUs enable you to organize your accounts into a hierarchy and make it easier for you to apply management controls. For a deep dive on OU best practices you can read the Best Practices for Organizational Units with AWS Organizations blog post.

You can either specify the organization itself, or individual OUs. By default the StackSet will be deployed to all AWS accounts that are part of the OU. If the OU is nested it will also deploy to all accounts that are part of any nested OUs.

For example, given the following org hierarchy

graph TD
  root-->ou-1;
  root-->ou-2;
  ou-1-->ou-3;
  ou-1-->ou-4;
  ou-3-->account-1;
  ou-3-->account-2;
  ou-4-->account-4;
  ou-2-->account-3;
  ou-2-->account-5;

You could deploy to all AWS accounts under OUs ou-1, ou-3, ou-4 by specifying the following:

stack = Stack()
stack_set_stack = StackSetStack(stack, "MyStackSet")

StackSet(stack, "StackSet",
    target=StackSetTarget.from_organizational_units(
        regions=["us-east-1"],
        organizational_units=["ou-1"]
    ),
    template=StackSetTemplate.from_stack_set_stack(stack_set_stack)
)

This would deploy the StackSet to account-1, account-2, account-4.

If there are specific AWS accounts that are part of the specified OU hierarchy that you would like to exclude, this can be done by specifying excludeAccounts.

stack = Stack()
stack_set_stack = StackSetStack(stack, "MyStackSet")

StackSet(stack, "StackSet",
    target=StackSetTarget.from_organizational_units(
        regions=["us-east-1"],
        organizational_units=["ou-1"],
        exclude_accounts=["account-2"]
    ),
    template=StackSetTemplate.from_stack_set_stack(stack_set_stack)
)

This would deploy only to account-1 & account-4, and would exclude account-2.

Sometimes you might have individual accounts that you would like to deploy the StackSet to, but you do not want to include the entire OU. To do that you can specify additionalAccounts.

stack = Stack()
stack_set_stack = StackSetStack(stack, "MyStackSet")

StackSet(stack, "StackSet",
    target=StackSetTarget.from_organizational_units(
        regions=["us-east-1"],
        organizational_units=["ou-1"],
        additional_accounts=["account-5"]
    ),
    template=StackSetTemplate.from_stack_set_stack(stack_set_stack)
)

This would deploy the StackSet to account-1, account-2, account-4 & account-5.

StackSet permissions

There are two modes for managing StackSet permissions (i.e. where StackSets can deploy & what resources they can create). A StackSet can either be Service Managed or Self Managed.

You can control this through the deploymentType parameter.

Service Managed

When a StackSet is service managed, the permissions are managed by AWS Organizations. This allows the StackSet to deploy the Stack to any account within the organization. In addition, the StackSet will be able to create any type of resource.

stack = Stack()
stack_set_stack = StackSetStack(stack, "MyStackSet")

StackSet(stack, "StackSet",
    target=StackSetTarget.from_organizational_units(
        regions=["us-east-1"],
        organizational_units=["ou-1"]
    ),
    deployment_type=DeploymentType.service_managed(),
    template=StackSetTemplate.from_stack_set_stack(stack_set_stack)
)

When you specify serviceManaged deployment type, automatic deployments are enabled by default. Automatic deployments allow the StackSet to be automatically deployed to or deleted from AWS accounts when they are added or removed from the specified organizational units.

Deploying StackSets using CDK Pipelines

You can also deploy StackSets using CDK Pipelines

Below is an example of a Pipeline that deploys from a central account. It also defines separate stages for each "environment" so that you can first test out the stackset in pre-prod environments.

This would be an automated way of deploying the bootstrap stack described in this blog post.

# app: App


class BootstrapStage(Stage):
    def __init__(self, scope, id, *, initialBootstrapTarget, stacksetName=None, env=None, outdir=None, stageName=None):
        super().__init__(scope, id, initialBootstrapTarget=initialBootstrapTarget, stacksetName=stacksetName, env=env, outdir=outdir, stageName=stageName)

        stack = Stack(self, "BootstrapStackSet")

        bootstrap = Bootstrap(stack, "CDKToolkit")

        stack_set = StackSet(stack, "StackSet",
            template=StackSetTemplate.from_stack_set_stack(bootstrap),
            target=initial_bootstrap_target,
            capabilities=[Capability.NAMED_IAM],
            managed_execution=True,
            stack_set_name=stackset_name,
            deployment_type=DeploymentType.service_managed(
                delegated_admin=True,
                auto_deploy_enabled=True,
                auto_deploy_retain_stacks=False
            ),
            operation_preferences=OperationPreferences(
                region_concurrency_type=RegionConcurrencyType.PARALLEL,
                max_concurrent_percentage=100,
                failure_tolerance_percentage=99
            )
        )

pipeline = pipelines.CodePipeline(self, "BootstrapPipeline",
    synth=pipelines.ShellStep("Synth",
        commands=["yarn install --frozen-lockfile", "npx cdk synth"
        ],
        input=pipelines.CodePipelineSource.connection("myorg/myrepo", "main",
            connection_arn="arn:aws:codestar-connections:us-east-2:111111111111:connection/ca65d487-ca6e-41cc-aab2-645db37fdb2b"
        )
    ),
    self_mutation=True
)

regions = ["us-east-1", "us-east-2", "us-west-2", "eu-west-2", "eu-west-1", "ap-south-1", "ap-southeast-1"
]

pipeline.add_stage(
    BootstrapStage(app, "DevBootstrap",
        env=Environment(
            region="us-east-1",
            account="111111111111"
        ),
        stackset_name="CDKToolkit-dev",
        initial_bootstrap_target=StackSetTarget.from_organizational_units(
            regions=regions,
            organizational_units=["ou-hrza-ar333427"]
        )
    ))

pipeline.add_stage(
    BootstrapStage(app, "ProdBootstrap",
        env=Environment(
            region="us-east-1",
            account="111111111111"
        ),
        stackset_name="CDKToolkit-prd",
        initial_bootstrap_target=StackSetTarget.from_organizational_units(
            regions=regions,
            organizational_units=["ou-hrza-bb999427", "ou-hraa-ar111127"]
        )
    ))

Project details


Release history Release notifications | RSS feed

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

cdk-stacksets-0.0.35.tar.gz (94.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

cdk_stacksets-0.0.35-py3-none-any.whl (92.1 kB view details)

Uploaded Python 3

File details

Details for the file cdk-stacksets-0.0.35.tar.gz.

File metadata

  • Download URL: cdk-stacksets-0.0.35.tar.gz
  • Upload date:
  • Size: 94.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/4.0.2 CPython/3.11.2

File hashes

Hashes for cdk-stacksets-0.0.35.tar.gz
Algorithm Hash digest
SHA256 991e37e670775542d31fa28c50c752ec0e0d08a83fb89fe83b819e6a7496d17f
MD5 d09908900c86e0f978965eb24a9fb065
BLAKE2b-256 54d24e67881350a7852ac0d65bb684dbb50353cd7cae21a88e3e445431518358

See more details on using hashes here.

File details

Details for the file cdk_stacksets-0.0.35-py3-none-any.whl.

File metadata

  • Download URL: cdk_stacksets-0.0.35-py3-none-any.whl
  • Upload date:
  • Size: 92.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/4.0.2 CPython/3.11.2

File hashes

Hashes for cdk_stacksets-0.0.35-py3-none-any.whl
Algorithm Hash digest
SHA256 b8d9cd47acc2fc4fde7e0debaeb69fd6a049444d4f002d0e932a948cf2b93c9d
MD5 2d690754616af1d5a501d76f423eb8e0
BLAKE2b-256 d82b65261c60fd4529044e4ba277b5cc888a9bc4dbe7462496da97dcde825be1

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page