Skip to content

Commit

Permalink
feat(app-staging-synthesizer): select different bootstrap region (#26129
Browse files Browse the repository at this point in the history
)

Allow selecting the region that was bootstrapped to be distinct from the region that the stack is being deployed to.

Also in this PR, clarify the README.

----

*By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
  • Loading branch information
rix0rrr authored Jun 27, 2023
1 parent 40871ec commit 2fec6a4
Show file tree
Hide file tree
Showing 4 changed files with 177 additions and 123 deletions.
172 changes: 57 additions & 115 deletions packages/@aws-cdk/app-staging-synthesizer-alpha/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,106 +48,44 @@ in your account.

## Bootstrap Model

Our current bootstrap model looks like this, when you run `cdk bootstrap aws://<account>/<region>` :

```text
┌───────────────────────────────────┐┌────────────────────────┐┌────────────────────────┐
│ ││ ││ │
│ ││ ││ │
│ ┌───────────────┐ ││ ┌──────────────┐ ││ ┌──────────────┐ │
│ │Bootstrap Stack│ ││ │ CDK App 1 │ ││ │ CDK App 2 │ │
│ └───────────────┘ ││ └──────────────┘ ││ └──────────────┘ │
│ ││ ││ │
│ ││ ││ │
│ ┌───────────────────────────┐ ││ ┌────────────┐ ││ │
│ │IAM Role for CFN execution │ ││┌────│ S3 Asset │ ││ │
│ │ IAM Role for lookup │ │││ └────────────┘ ││ │
│ │ IAM Role for deployment │ │││ ││ │
│ └───────────────────────────┘ │││ ││ ┌─────────────┐ │
│ │││ ┌──────────┼┼─────│ S3 Asset │ │
│ │││ │ ││ └─────────────┘ │
│ ┌───────────────────────────────┐ │││ │ ││ │
│ │ IAM Role for File Publishing │ │││ │ ││ │
│ │ IAM Role for Image Publishing │ │││ │ ││ │
│ └───────────────────────────────┘ │││ │ ││ │
│ │││ │ ││ │
│ ┌─────────────────────────────┐ │││ │ ││ │
│ │S3 Bucket for Staging Assets │ │││ │ ││ │
│ │ KMS Key encryption │◀─┼┼┴────────────┘ ││ ┌────────────┐ │
│ └─────────────────────────────┘ ││ ┌──────────┼┼───── │ ECR Asset │ │
│ ││ │ ││ └────────────┘ │
│ ││ │ ││ │
│┌─────────────────────────────────┐││ │ ││ │
││ECR Repository for Staging Assets◀┼┼─────────────┘ ││ │
│└─────────────────────────────────┘││ ││ │
│ ││ ││ │
│ ││ ││ │
│ ││ ││ │
│ ││ ││ │
│ ││ ││ │
│ ││ ││ │
└───────────────────────────────────┘└────────────────────────┘└────────────────────────┘
```

Your CDK Application utilizes these resources when deploying. For example, if you have a file asset,
it gets uploaded to the S3 Staging Bucket using the File Publishing Role when you run `cdk deploy`.

This library introduces an alternate model to bootstrapping, by splitting out essential CloudFormation IAM roles
and staging resources. There will still be a Bootstrap Stack, but this will only contain IAM roles necessary for
CloudFormation deployment. Each CDK App will instead be in charge of its own staging resources, including the
S3 Bucket, ECR Repositories, and associated IAM roles. It works like this:

The Staging Stack will contain, on a per-need basis,

- 1 S3 Bucket with KMS encryption for all file assets in the CDK App.
- An ECR Repository _per_ image (and its revisions).
- IAM roles with access to the Bucket and Repositories.

```text
┌─────────────────────────────┐┌───────────────────────────────────────┐┌───────────────────────────────────────┐
│ ││ ││ │
│ ┌───────────────┐ ││ ┌──────────────┐ ││ ┌──────────────┐ │
│ │Bootstrap Stack│ ││ │ CDK App 1 │ ││ │ CDK App 2 │ │
│ └───────────────┘ ││ └──────────────┘ ││ └──────────────┘ │
│ ││┌──────────────────┐ ││┌──────────────────┐ │
│ │││ ┌──────────────┐ │ │││ ┌──────────────┐ │ │
│ │││ │Staging Stack │ │ │││ │Staging Stack │ │ │
│ │││ └──────────────┘ │ │││ └──────────────┘ │ │
│ │││ │ │││ │ │
│ │││ │ │││ │ │
│ │││┌────────────────┐│ ┌────────────┐│││┌────────────────┐│ ┌────────────┐│
│ ││││ IAM Role for ││ ┌───│ S3 Asset │││││ IAM Role for ││ ┌───│ S3 Asset ││
│ ││││File Publishing ││ │ └────────────┘││││File Publishing ││ │ └────────────┘│
│ │││└────────────────┘│ │ ││││ IAM Role for ││ │ │
│ │││ │ │ ││││Image Publishing││ │ │
│┌───────────────────────────┐│││ │ │ │││└────────────────┘│ │ │
││IAM Role for CFN execution ││││ │ │ │││ │ │ │
││ IAM Role for lookup ││││ │ │ │││ │ │ │
││ IAM Role for deployment ││││┌────────────────┐│ │ │││┌────────────────┐│ │ │
│└───────────────────────────┘││││ S3 Bucket for ││ │ ││││ S3 Bucket for ││ │ │
│ ││││ Staging Assets │◀─┘ ││││ Staging Assets │◀─┘ │
│ │││└────────────────┘│ │││└────────────────┘│ ┌───────────┐│
│ │││ │ │││ │ ┌───│ ECR Asset ││
│ │││ │ │││┌────────────────┐│ │ └───────────┘│
│ │││ │ ││││ ECR Repository ││ │ │
│ │││ │ ││││ for Staging │◀──┘ │
│ │││ │ ││││ Assets ││ │
│ │││ │ │││└────────────────┘│ │
│ │││ │ │││ │ │
│ │││ │ │││ │ │
│ │││ │ │││ │ │
│ │││ │ │││ │ │
│ │││ │ │││ │ │
│ ││└──────────────────┘ ││└──────────────────┘ │
└─────────────────────────────┘└───────────────────────────────────────┘└───────────────────────────────────────┘
```

This allows staging resources to be created when needed next to the CDK App. It has the following
benefits:

In our default bootstrapping process, when you run `cdk bootstrap aws://<account>/<region>`, the following
resources are created:

- It creates Roles to assume for cross-account deployments and for Pipeline deployments;
- It creates staging resources: a global S3 bucket and global ECR repository to hold CDK assets;
- It creates Roles to write to the S3 bucket and ECR repository;

Because the bootstrapping resources include regional resources, you need to bootstrap
every region you plan to deploy to individually. All assets of all CDK apps deploying
to that account and region will be written to the single S3 Bucket and ECR repository.

By using the synthesizer in this library, instead of the
`DefaultStackSynthesizer`, a different set of staging resources will be created
for every CDK application, and they will be created automatically as part of a
regular deployment, in a separate Stack that is deployed before your application
Stacks. The staging resources will be one S3 bucket, and *one ECR repository per
image*, and Roles necessary to access those buckets and ECR repositories. The
Roles from the default bootstrap stack are still used (though their use can be
turned off).

This has the following advantages:

- Because staging resources are now application-specific, they can be fully cleaned up when you clean up
the application.
- Because there is now one ECR repository per image instead of one ECR repository for all images, it is
possible to effectively use ECR life cycle rules (for example, retain only the most recent 5 images)
to cut down on storage costs.
- Resources between separate CDK Apps are separated so they can be cleaned up and lifecycle
controlled individually.
- Users have a familiar way to customize staging resources in the CDK Application.
controlled individually.
- Because the only shared bootstrapping resources required are Roles, which are global resources,
you now only need to bootstrap every account in one Region (instead of every Region). This makes it
easier to do with CloudFormation StackSets.

For the deployment roles, this synthesizer still uses the Roles from the default
bootstrap stack, and nothing else. The staging resources from that bootstrap
stack will be unused. You can customize the template to remove those resources
if you prefer. In the future, we will provide a bootstrap stack template with
only those Roles, specifically for use with this synthesizer.

## Using the Default Staging Stack per Environment

Expand All @@ -159,6 +97,10 @@ its staging resources. To use this kind of synthesizer, use `AppStagingSynthesiz
const app = new App({
defaultStackSynthesizer: AppStagingSynthesizer.defaultResources({
appId: 'my-app-id',

// The following line is optional. By default it is assumed you have bootstrapped in the same
// region(s) as the stack(s) you are deploying.
deploymentIdentities: DeploymentIdentities.defaultBootstrapRoles({ bootstrapRegion: 'us-east-1' }),
}),
});
```
Expand Down Expand Up @@ -232,7 +174,7 @@ assumable by the deployment role. You can also specify an existing IAM role for
const app = new App({
defaultStackSynthesizer: AppStagingSynthesizer.defaultResources({
appId: 'my-app-id',
fileAssetPublishingRole: BootstrapRole.fromRoleArn('arn:aws:iam::123456789012:role/S3Access'),
fileAssetPublishingRole: BootstrapRole.fromRoleArn('arn:aws:iam::123456789012:role/S3Access'),
imageAssetPublishingRole: BootstrapRole.fromRoleArn('arn:aws:iam::123456789012:role/ECRAccess'),
}),
});
Expand All @@ -242,14 +184,14 @@ const app = new App({

There are two types of assets:

- Assets used only during deployment. These are used to hand off a large piece of data to another
service, that will make a private copy of that data. After deployment, the asset is only necessary for
a potential future rollback.
- Assets used only during deployment. These are used to hand off a large piece of data to another
service, that will make a private copy of that data. After deployment, the asset is only necessary for
a potential future rollback.
- Assets accessed throughout the running life time of the application.

Examples of assets that are only used at deploy time are CloudFormation Templates and Lambda Code
bundles. Examples of assets accessed throughout the life time of the application are script files
downloaded to run in a CodeBuild Project, or on EC2 instance startup. ECR images are always application
bundles. Examples of assets accessed throughout the life time of the application are script files
downloaded to run in a CodeBuild Project, or on EC2 instance startup. ECR images are always application
life-time assets. S3 deploy time assets are stored with a `deploy-time/` prefix, and a lifecycle rule will collect them after a configurable number of days.

Lambda assets are by default marked as deploy time assets:
Expand All @@ -275,9 +217,9 @@ const asset = new Asset(stack, 'deploy-time-asset', {
});
```

By default, we store deploy time assets for 30 days, but you can change this number by specifying
By default, we store deploy time assets for 30 days, but you can change this number by specifying
`deployTimeFileAssetLifetime`. The number you specify here is how long you will be able to roll back
to a previous version of an application just by doing a CloudFormation deployment with the old
to a previous version of an application just by doing a CloudFormation deployment with the old
template, without rebuilding and republishing assets.

```ts
Expand Down Expand Up @@ -316,7 +258,7 @@ you can subclass `DefaultStagingStack`.
```ts
interface CustomStagingStackOptions extends DefaultStagingStackOptions {}

class CustomStagingStack extends DefaultStagingStack {
class CustomStagingStack extends DefaultStagingStack {
}
```

Expand Down Expand Up @@ -389,13 +331,13 @@ const app = new App({

Since this module is experimental, there are some known limitations:

- Currently this module does not support CDK Pipelines. You must deploy CDK Apps using this
synthesizer via `cdk deploy`.
- Currently this module does not support CDK Pipelines. You must deploy CDK Apps using this
synthesizer via `cdk deploy`. Please upvote [this issue](https://github.com/aws/aws-cdk/issues/26118)
to indicate you want this.
- This synthesizer only needs a bootstrap stack with Roles, without staging resources. We
haven't written such a bootstrap stack yet; at the moment you can use the existing modern
bootstrap stack, the staging resources in them will just go unused.
bootstrap stack, the staging resources in them will just go unused. You can customize the
template to remove them if desired.
- Due to limitations on the CloudFormation template size, CDK Applications can have
at most 38 independent ECR images.
- When you run `cdk destroy` (for example during testing), the staging bucket and ECR
repositories will be left behind because CloudFormation cannot clean up non-empty resources.
You must deploy those resources manually if you want to redeploy again using the same `appId`.
at most 38 independent ECR images. Please upvote [this issue](https://github.com/aws/aws-cdk/issues/26119)
if you need more than this.
Loading

0 comments on commit 2fec6a4

Please sign in to comment.