Skip to content

Commit

Permalink
Merge branch 'master' into benisrae/eks-fix-version-update
Browse files Browse the repository at this point in the history
  • Loading branch information
Elad Ben-Israel committed Apr 23, 2020
2 parents 2de3eff + 98429e0 commit 1be46b6
Show file tree
Hide file tree
Showing 22 changed files with 2,257 additions and 24 deletions.
3 changes: 2 additions & 1 deletion pack.sh
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ rm -fr ${distdir}
mkdir -p ${distdir}

# Split out jsii and non-jsii packages. Jsii packages will be built all at once.
# Non-jsii packages will be run individually.
# Non-jsii packages will be run individually. Note that currently the monoCDK
# package is handled as non-jsii because of the way it is packaged.
echo "Collecting package list..." >&2
scripts/list-packages $TMPDIR/jsii.txt $TMPDIR/nonjsii.txt

Expand Down
72 changes: 70 additions & 2 deletions packages/@aws-cdk/aws-backup/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,76 @@
---
<!--END STABILITY BANNER-->

This module is part of the [AWS Cloud Development Kit](https://github.com/aws/aws-cdk) project.
AWS Backup is a fully managed backup service that makes it easy to centralize and automate the backup of data across AWS services in the cloud and on premises. Using AWS Backup, you can configure backup policies and monitor backup activity for your AWS resources in one place.

### Backup plan and selection

In AWS Backup, a *backup plan* is a policy expression that defines when and how you want to back up your AWS resources, such as Amazon DynamoDB tables or Amazon Elastic File System (Amazon EFS) file systems. You can assign resources to backup plans, and AWS Backup automatically backs up and retains backups for those resources according to the backup plan. You can create multiple backup plans if you have workloads with different backup requirements.

This module provides ready-made backup plans (similar to the console experience):

```ts
import * as backup from '@aws-cdk/aws-backup';

// Daily, weekly and monthly with 5 year retention
const plan = backup.BackupPlan.dailyWeeklyMonthly5YearRetention(this, 'Plan');
```

Assigning resources to a plan can be done with `addSelection()`:

```ts
plan.addSelection('Selection', {
resources: [
backup.BackupResource.fromDynamoDbTable(myTable), // A DynamoDB table
backup.BackupResource.fromTag('stage', 'prod'), // All resources that are tagged stage=prod in the region/account
backup.BackupResource.fromConstruct(myCoolConstruct), // All backupable resources in `myCoolConstruct`
]
})
```

If not specified, a new IAM role with a managed policy for backup will be
created for the selection. The `BackupSelection` implements `IGrantable`.

To add rules to a plan, use `addRule()`:

```ts
import backup = require('@aws-cdk/aws-backup');
plan.addRule(new BackupPlanRule({
completionWindow: Duration.hours(2),
startWindow: Duration.hours(1),
scheduleExpression: events.Schedule.cron({ // Only cron expressions are supported
day: '15',
hour: '3',
minute: '30'
}),
moveToColdStorageAfter: Duration.days(30)
}));
```

Ready-made rules are also available:

```ts
plan.addRule(BackupPlanRule.daily());
plan.addRule(BackupPlanRule.weekly());
```

By default a new [vault](#Backup-vault) is created when creating a plan.
It is also possible to specify a vault either at the plan level or at the
rule level.

```ts
const plan = backup.BackupPlan.daily35DayRetention(this, 'Plan', myVault); // Use `myVault` for all plan rules
plan.addRule(BackupPlanRule.monthly1Year(otherVault)); // Use `otherVault` for this specific rule
```

### Backup vault
In AWS Backup, a *backup vault* is a container that you organize your backups in. You can use backup vaults to set the AWS Key Management Service (AWS KMS) encryption key that is used to encrypt backups in the backup vault and to control access to the backups in the backup vault. If you require different encryption keys or access policies for different groups of backups, you can optionally create multiple backup vaults.

```ts
const vault = new BackupVault(stack, 'Vault', {
encryptionKey: myKey, // Custom encryption key
notificationTopic: myTopic, // Send all vault events to this SNS topic
});
```

A vault has a default `RemovalPolicy` set to `RETAIN`. Note that removing a vault
that contains recovery points will fail.
52 changes: 52 additions & 0 deletions packages/@aws-cdk/aws-backup/lib/backupable-resources-collector.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import * as dynamodb from '@aws-cdk/aws-dynamodb';
import * as ec2 from '@aws-cdk/aws-ec2';
import * as efs from '@aws-cdk/aws-efs';
import * as rds from '@aws-cdk/aws-rds';
import { IAspect, IConstruct, Stack } from '@aws-cdk/core';

export class BackupableResourcesCollector implements IAspect {
public readonly resources: string[] = [];

public visit(node: IConstruct) {
if (node instanceof efs.CfnFileSystem) {
this.resources.push(Stack.of(node).formatArn({
service: 'elasticfilesystem',
resource: 'file-system',
resourceName: node.ref,
}));
}

if (node instanceof dynamodb.CfnTable) {
this.resources.push(Stack.of(node).formatArn({
service: 'dynamodb',
resource: 'table',
resourceName: node.ref,
}));
}

if (node instanceof ec2.CfnInstance) {
this.resources.push(Stack.of(node).formatArn({
service: 'ec2',
resource: 'instance',
resourceName: node.ref,
}));
}

if (node instanceof ec2.CfnVolume) {
this.resources.push(Stack.of(node).formatArn({
service: 'ec2',
resource: 'volume',
resourceName: node.ref,
}));
}

if (node instanceof rds.CfnDBInstance) {
this.resources.push(Stack.of(node).formatArn({
service: 'rds',
resource: 'db',
sep: ':',
resourceName: node.ref,
}));
}
}
}
6 changes: 6 additions & 0 deletions packages/@aws-cdk/aws-backup/lib/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,8 @@
export * from './vault';
export * from './plan';
export * from './rule';
export * from './selection';
export * from './resource';

// AWS::Backup CloudFormation Resources:
export * from './backup.generated';
201 changes: 201 additions & 0 deletions packages/@aws-cdk/aws-backup/lib/plan.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,201 @@
import { Construct, IResource, Lazy, Resource } from '@aws-cdk/core';
import { CfnBackupPlan } from './backup.generated';
import { BackupPlanRule } from './rule';
import { BackupSelection, BackupSelectionOptions } from './selection';
import { BackupVault, IBackupVault } from './vault';

/**
* A backup plan
*/
export interface IBackupPlan extends IResource {
/**
* The identifier of the backup plan.
*
* @attribute
*/
readonly backupPlanId: string;
}

/**
* Properties for a BackupPlan
*/
export interface BackupPlanProps {
/**
* The display name of the backup plan.
*
* @default - A CDK generated name
*/
readonly backupPlanName?: string;

/**
* The backup vault where backups are stored
*
* @default - use the vault defined at the rule level. If not defined a new
* common vault for the plan will be created
*/
readonly backupVault?: IBackupVault;

/**
* Rules for the backup plan. Use `addRule()` to add rules after
* instantiation.
*
* @default - use `addRule()` to add rules
*/
readonly backupPlanRules?: BackupPlanRule[];
}

/**
* A backup plan
*/
export class BackupPlan extends Resource implements IBackupPlan {
/**
* Import an existing backup plan
*/
public static fromBackupPlanId(scope: Construct, id: string, backupPlanId: string): IBackupPlan {
class Import extends Resource implements IBackupPlan {
public readonly backupPlanId = backupPlanId;
}
return new Import(scope, id);
}

/**
* Daily with 35 day retention
*/
public static daily35DayRetention(scope: Construct, id: string, backupVault?: IBackupVault) {
const plan = new BackupPlan(scope, id, { backupVault });
plan.addRule(BackupPlanRule.daily());
return plan;
}

/**
* Daily and monthly with 1 year retention
*/
public static dailyMonthly1YearRetention(scope: Construct, id: string, backupVault?: IBackupVault) {
const plan = new BackupPlan(scope, id, { backupVault });
plan.addRule(BackupPlanRule.daily());
plan.addRule(BackupPlanRule.monthly1Year());
return plan;
}

/**
* Daily, weekly and monthly with 5 year retention
*/
public static dailyWeeklyMonthly5YearRetention(scope: Construct, id: string, backupVault?: IBackupVault) {
const plan = new BackupPlan(scope, id, { backupVault });
plan.addRule(BackupPlanRule.daily());
plan.addRule(BackupPlanRule.weekly());
plan.addRule(BackupPlanRule.monthly5Year());
return plan;
}

/**
* Daily, weekly and monthly with 7 year retention
*/
public static dailyWeeklyMonthly7YearRetention(scope: Construct, id: string, backupVault?: IBackupVault) {
const plan = new BackupPlan(scope, id, { backupVault });
plan.addRule(BackupPlanRule.daily());
plan.addRule(BackupPlanRule.weekly());
plan.addRule(BackupPlanRule.monthly7Year());
return plan;
}

public readonly backupPlanId: string;

/**
* The ARN of the backup plan
*
* @attribute
*/
public readonly backupPlanArn: string;

/**
* Version Id
*
* @attribute
*/
public readonly versionId: string;

private readonly rules: CfnBackupPlan.BackupRuleResourceTypeProperty[] = [];
private _backupVault?: IBackupVault;

constructor(scope: Construct, id: string, props: BackupPlanProps = {}) {
super(scope, id);

const plan = new CfnBackupPlan(this, 'Resource', {
backupPlan: {
backupPlanName: props.backupPlanName || id,
backupPlanRule: Lazy.anyValue({ produce: () => this.rules }, { omitEmptyArray: true }),
},
});

this.backupPlanId = plan.attrBackupPlanId;
this.backupPlanArn = plan.attrBackupPlanArn;
this.versionId = plan.attrVersionId;

this._backupVault = props.backupVault;

for (const rule of props.backupPlanRules || []) {
this.addRule(rule);
}
}

/**
* Adds a rule to a plan
*
* @param rule the rule to add
*/
public addRule(rule: BackupPlanRule) {
let vault: IBackupVault;
if (rule.props.backupVault) {
vault = rule.props.backupVault;
} else if (this._backupVault) {
vault = this._backupVault;
} else {
this._backupVault = new BackupVault(this, 'Vault');
vault = this._backupVault;
}

this.rules.push({
completionWindowMinutes: rule.props.completionWindow?.toMinutes(),
lifecycle: (rule.props.deleteAfter || rule.props.moveToColdStorageAfter) && {
deleteAfterDays: rule.props.deleteAfter?.toDays(),
moveToColdStorageAfterDays: rule.props.moveToColdStorageAfter?.toDays(),
},
ruleName: rule.props.ruleName ?? `${this.node.id}Rule${this.rules.length}`,
scheduleExpression: rule.props.scheduleExpression?.expressionString,
startWindowMinutes: rule.props.startWindow?.toMinutes(),
targetBackupVault: vault.backupVaultName,
});
}

/**
* The backup vault where backups are stored if not defined at
* the rule level
*/
public get backupVault(): IBackupVault {
if (!this._backupVault) {
// This cannot happen but is here to make TypeScript happy
throw new Error('No backup vault!');
}

return this._backupVault;
}

/**
* Adds a selection to this plan
*/
public addSelection(id: string, options: BackupSelectionOptions): BackupSelection {
return new BackupSelection(this, id, {
backupPlan: this,
...options,
});
}

protected validate() {
if (this.rules.length === 0) {
return ['A backup plan must have at least 1 rule.'];
}

return [];
}
}

0 comments on commit 1be46b6

Please sign in to comment.