io.github.cdklabs.cdk.stacksets.package-info Maven / Gradle / Ivy
/**
* 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 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 = new Stack();
* StackSetStack stackSetStack = new StackSetStack(stack, "MyStackSet");
*
* StackSet.Builder.create(stack, "StackSet")
* .target(StackSetTarget.fromAccounts(AccountsTargetOptions.builder()
* .regions(List.of("us-east-1"))
* .accounts(List.of("11111111111"))
* .parameterOverrides(Map.of(
* "SomeParam", "overrideValue"))
* .build()))
* .template(StackSetTemplate.fromStackSetStack(stackSetStack))
* .build();
*
*
*
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. StackSetStack
s 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
*
*
* - Does not support Docker container assets
*
*
* Once you create a StackSetStack
you can create resources within the stack.
*
*
* Stack stack = new Stack();
* StackSetStack stackSetStack = new StackSetStack(stack, "StackSet");
*
* Role.Builder.create(stackSetStack, "MyRole")
* .assumedBy(new ServicePrincipal("myservice.amazonaws.com"))
* .build();
*
*
* Or
*
*
* public class MyStackSet extends StackSetStack {
* public MyStackSet(Construct scope, String id) {
* super(scope, id);
*
* Role.Builder.create(this, "MyRole")
* .assumedBy(new ServicePrincipal("myservice.amazonaws.com"))
* .build();
* }
* }
*
*
*
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 = new Stack();
* StackSetStack stackSetStack = new StackSetStack(stack, "MyStackSet");
*
* StackSet.Builder.create(stack, "StackSet")
* .target(StackSetTarget.fromAccounts(AccountsTargetOptions.builder()
* .regions(List.of("us-east-1"))
* .accounts(List.of("11111111111"))
* .build()))
* .template(StackSetTemplate.fromStackSetStack(stackSetStack))
* .build();
*
*
*
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 = new Stack();
* StackSetStack stackSetStack = new StackSetStack(stack, "MyStackSet");
*
* StackSet.Builder.create(stack, "StackSet")
* .target(StackSetTarget.fromOrganizationalUnits(OrganizationsTargetOptions.builder()
* .regions(List.of("us-east-1"))
* .organizationalUnits(List.of("ou-1"))
* .build()))
* .template(StackSetTemplate.fromStackSetStack(stackSetStack))
* .build();
*
*
* 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 = new Stack();
* StackSetStack stackSetStack = new StackSetStack(stack, "MyStackSet");
*
* StackSet.Builder.create(stack, "StackSet")
* .target(StackSetTarget.fromOrganizationalUnits(OrganizationsTargetOptions.builder()
* .regions(List.of("us-east-1"))
* .organizationalUnits(List.of("ou-1"))
* .excludeAccounts(List.of("account-2"))
* .build()))
* .template(StackSetTemplate.fromStackSetStack(stackSetStack))
* .build();
*
*
* 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 = new Stack();
* StackSetStack stackSetStack = new StackSetStack(stack, "MyStackSet");
*
* StackSet.Builder.create(stack, "StackSet")
* .target(StackSetTarget.fromOrganizationalUnits(OrganizationsTargetOptions.builder()
* .regions(List.of("us-east-1"))
* .organizationalUnits(List.of("ou-1"))
* .additionalAccounts(List.of("account-5"))
* .build()))
* .template(StackSetTemplate.fromStackSetStack(stackSetStack))
* .build();
*
*
* 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 = new Stack();
* StackSetStack stackSetStack = new StackSetStack(stack, "MyStackSet");
*
* StackSet.Builder.create(stack, "StackSet")
* .target(StackSetTarget.fromOrganizationalUnits(OrganizationsTargetOptions.builder()
* .regions(List.of("us-east-1"))
* .organizationalUnits(List.of("ou-1"))
* .build()))
* .deploymentType(DeploymentType.serviceManaged())
* .template(StackSetTemplate.fromStackSetStack(stackSetStack))
* .build();
*
*
* 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.
*
*
Using File Assets
*
* You can use the StackSet's parent stack to facilitate file assets. Behind the scenes,
* this is accomplished using the BucketDeployment
construct from the
* aws_s3_deployment
module. You need to provide a bucket outside the scope of the CDK
* managed asset buckets and ensure you have persmissions for the target accounts to pull
* the artifacts from the supplied bucket.
*
* As a basic example, if using a serviceManaged
deployment, 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 bucket = Bucket.Builder.create(this, "Assets")
* .bucketName("cdkstacket-asset-bucket-xyz")
* .build();
*
* bucket.addToResourcePolicy(
* PolicyStatement.Builder.create()
* .actions(List.of("s3:Get*", "s3:List*"))
* .resources(List.of(bucket.arnForObjects("*"), bucket.getBucketArn()))
* .principals(List.of(new OrganizationPrincipal("o-xyz")))
* .build());
*
*
* Then pass as a prop to the StackSet stack:
*
*
* Bucket bucket;
*
* Stack stack = new Stack();
* StackSetStack stackSetStack = StackSetStack.Builder.create(stack, "MyStackSet")
* .assetBucket(bucket)
* .build();
*
*
* Then call new StackSet
as described in the sections above.
*
* 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 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;
*
* public class BootstrapStageProps extends StageProps {
* private StackSetTarget initialBootstrapTarget;
* public StackSetTarget getInitialBootstrapTarget() {
* return this.initialBootstrapTarget;
* }
* public BootstrapStageProps initialBootstrapTarget(StackSetTarget initialBootstrapTarget) {
* this.initialBootstrapTarget = initialBootstrapTarget;
* return this;
* }
* private String stacksetName;
* public String getStacksetName() {
* return this.stacksetName;
* }
* public BootstrapStageProps stacksetName(String stacksetName) {
* this.stacksetName = stacksetName;
* return this;
* }
* }
*
* public class BootstrapStage extends Stage {
* public BootstrapStage(Construct scope, String id, BootstrapStageProps props) {
* super(scope, id, props);
*
* Stack stack = new Stack(this, "BootstrapStackSet");
*
* Bootstrap bootstrap = new Bootstrap(stack, "CDKToolkit");
*
* StackSet stackSet = StackSet.Builder.create(stack, "StackSet")
* .template(StackSetTemplate.fromStackSetStack(bootstrap))
* .target(props.getInitialBootstrapTarget())
* .capabilities(List.of(Capability.NAMED_IAM))
* .managedExecution(true)
* .stackSetName(props.getStacksetName())
* .deploymentType(DeploymentType.serviceManaged(ServiceManagedOptions.builder()
* .delegatedAdmin(true)
* .autoDeployEnabled(true)
* .autoDeployRetainStacks(false)
* .build()))
* .operationPreferences(OperationPreferences.builder()
* .regionConcurrencyType(RegionConcurrencyType.PARALLEL)
* .maxConcurrentPercentage(100)
* .failureTolerancePercentage(99)
* .build())
* .build();
* }
* }
*
* CodePipeline pipeline = CodePipeline.Builder.create(this, "BootstrapPipeline")
* .synth(ShellStep.Builder.create("Synth")
* .commands(List.of("yarn install --frozen-lockfile", "npx cdk synth"))
* .input(CodePipelineSource.connection("myorg/myrepo", "main", ConnectionSourceOptions.builder()
* .connectionArn("arn:aws:codestar-connections:us-east-2:111111111111:connection/ca65d487-ca6e-41cc-aab2-645db37fdb2b")
* .build()))
* .build())
* .selfMutation(true)
* .build();
*
* String[] regions = List.of("us-east-1", "us-east-2", "us-west-2", "eu-west-2", "eu-west-1", "ap-south-1", "ap-southeast-1");
*
* pipeline.addStage(
* new BootstrapStage(app, "DevBootstrap", new BootstrapStageProps()
* .env(Environment.builder()
* .region("us-east-1")
* .account("111111111111")
* .build())
* .stacksetName("CDKToolkit-dev")
* .initialBootstrapTarget(StackSetTarget.fromOrganizationalUnits(OrganizationsTargetOptions.builder()
* .regions(regions)
* .organizationalUnits(List.of("ou-hrza-ar333427"))
* .build()))
* ));
*
* pipeline.addStage(
* new BootstrapStage(app, "ProdBootstrap", new BootstrapStageProps()
* .env(Environment.builder()
* .region("us-east-1")
* .account("111111111111")
* .build())
* .stacksetName("CDKToolkit-prd")
* .initialBootstrapTarget(StackSetTarget.fromOrganizationalUnits(OrganizationsTargetOptions.builder()
* .regions(regions)
* .organizationalUnits(List.of("ou-hrza-bb999427", "ou-hraa-ar111127"))
* .build()))
* ));
*
*/
@software.amazon.jsii.Stability(software.amazon.jsii.Stability.Level.Experimental)
package io.github.cdklabs.cdk.stacksets;