Package: cdk-stacksets
CDK StackSets Construct Library---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 theSemantic Versioningmodel 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))InstallingTypeScript/JavaScriptnpminstallcdk-stacksetsPythonpipinstallcdk-stacksetsJava//addthistoyourpom.xml<dependency><groupId>io.github.cdklabs</groupId><artifactId>cdk-stacksets</artifactId><version>0.0.0</version>//replacewithversion</dependency>.NETdotnetaddpackageCdklabsCdkStacksets--versionX.X.XGogogetcdk-stacksets-goCreating a StackSet StackStackSets 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 aStackSetStackthe same way that you declare a normalStack, but there
are a couple of differences.StackSetStacks have a couple of special requirements/limitations when
compared to Stacks.RequirementsMust be created in the scope of aStackMust be environment agnosticLimitationsDoes not support Docker container assetsOnce you create aStackSetStackyou 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"))OrclassMyStackSet(StackSetStack):def__init__(self,scope,id):super().__init__(scope,id)iam.Role(self,"MyRole",assumed_by=iam.ServicePrincipal("myservice.amazonaws.com"))Creating a StackSetAWS 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 definingwherethe StackSet should be deployed. You can either define individual accounts, or
you can define AWS Organizations organizational units.Deploying to individual accountsDeploying 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 unitsAWS 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 theBest Practices for Organizational Units with AWS Organizationsblog 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 hierarchygraph 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 OUsou-1,ou-3,ou-4by 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 toaccount-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 specifyingexcludeAccounts.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 toaccount-1&account-4, and would excludeaccount-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 specifyadditionalAccounts.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 toaccount-1,account-2,account-4&account-5.StackSet permissionsThere are two modes for managing StackSet permissions (i.e.whereStackSets can deploy &whatresources they can create).
A StackSet can either beService ManagedorSelf Managed.You can control this through thedeploymentTypeparameter.Service ManagedWhen a StackSet is service managed, the permissions are managed by AWS Organizations. This allows the StackSet to deploy the Stack toanyaccount within the organization. In addition, the StackSet will be able to createanytype 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 specifyserviceManageddeployment 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.Using File AssetsYou can use the StackSet's parent stack to facilitate file assets. Behind the scenes,
this is accomplished using theBucketDeploymentconstruct from theaws_s3_deploymentmodule. You need to provide a list of buckets outside the scope of the CDK
managed asset buckets and ensure you have permissions for the target accounts to pull
the artifacts from the supplied bucket(s).As a basic example, if using aserviceManageddeployment, you just need to give read
access to the Organization. You can create the asset bucket in the parent stack, or another
stack in the same app and pass the object as a prop. Or, import an existing bucket as needed.If creating in the parent or sibling stack you could create and export similar to this:bucket=s3.Bucket(self,"Assets",bucket_name="prefix-us-east-1")bucket.add_to_resource_policy(iam.PolicyStatement(actions=["s3:Get*","s3:List*"],resources=[bucket.arn_for_objects("*"),bucket.bucket_arn],principals=[iam.OrganizationPrincipal("o-xyz")]))Then pass as a prop to the StackSet stack:# bucket: s3.Bucketstack=Stack()stack_set_stack=StackSetStack(stack,"MyStackSet",asset_buckets=[bucket],asset_bucket_prefix="prefix")To faciliate multi region deployments, there is an assetBucketPrefix property. This
gets added to the region the Stack Set is deployed to. The stack synthesis for
the Stack Set would look for a bucket namedprefix-{Region}in the example
above.{Region}is whatever region you are deploying the Stack Set to as
defined in your target property of the StackSet. You will need to ensure the
bucket name is correct based on what was previously created and then passed in.You can use self-managed StackSet deployments with file assets too but will
need to ensure all target accounts roles will have access to the central asset
bucket you pass as the property.Deploying StackSets using CDK PipelinesYou can also deploy StackSets usingCDK PipelinesBelow 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 inthis blog
post.# app: AppclassBootstrapStage(Stage):def__init__(self,scope,id,*,initialBootstrapTarget,stacksetName=None,env=None,outdir=None,stageName=None,permissionsBoundary=None,policyValidationBeta1=None):super().__init__(scope,id,initialBootstrapTarget=initialBootstrapTarget,stacksetName=stacksetName,env=env,outdir=outdir,stageName=stageName,permissionsBoundary=permissionsBoundary,policyValidationBeta1=policyValidationBeta1)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"])))