Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(config): add constructs for managed and custom rules #2326

Merged
merged 23 commits into from
May 23, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 70 additions & 0 deletions packages/@aws-cdk/aws-config/README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,72 @@
## The CDK Construct Library for AWS Config
This module is part of the [AWS Cloud Development Kit](https://github.com/awslabs/aws-cdk) project.

Supported:
* Config rules

Not supported
* Configuration recoder
* Delivery channel
* Aggregation

### Rules

#### AWS managed rules
To set up a managed rule, define a `ManagedRule` and specify its identifier:

```ts
new ManagedRule(this, 'AccessKeysRotated', {
identifier: 'ACCESS_KEYS_ROTATED'
});
```

Available identifiers and parameters are listed in the [List of AWS Config Managed Rules](https://docs.aws.amazon.com/config/latest/developerguide/managed-rules-by-aws-config.html).


Higher level constructs for managed rules are available, see [Managed Rules](https://github.com/awslabs/aws-cdk/blob/master/packages/%40aws-cdk/aws-config/lib/managed-rules.ts). Prefer to use those constructs when available (PRs welcome to add more of those).

#### Custom rules
To set up a custom rule, define a `CustomRule` and specify the Lambda Function to run and the trigger types:

```ts
new CustomRule(this, 'CustomRule', {
lambdaFunction: myFn,
configurationChanges: true,
periodic: true
});
```

#### Restricting the scope
By default rules are triggered by changes to all [resources](https://docs.aws.amazon.com/config/latest/developerguide/resource-config-reference.html#supported-resources). Use the `scopeToResource()`, `scopeToResources()` or `scopeToTag()` methods to restrict the scope of both managed and custom rules:

```ts
const sshRule = new ManagedRule(this, 'SSH', {
identifier: 'INCOMING_SSH_DISABLED'
});

// Restrict to a specific security group
rule.scopeToResource('AWS::EC2::SecurityGroup', 'sg-1234567890abcdefgh');

const customRule = new CustomRule(this, 'CustomRule', {
lambdaFunction: myFn,
configurationChanges: true
});

// Restrict to a specific tag
customRule.scopeToTag('Cost Center', 'MyApp');
```

Only one type of scope restriction can be added to a rule (the last call to `scopeToXxx()` sets the scope).

#### Events
To define Amazon CloudWatch event rules, use the `onComplianceChange()` or `onReEvaluationStatus()` methods:

```ts
const rule = new CloudFormationStackDriftDetectionCheck(this, 'Drift');
rule.onComplianceChange(topic);
```

#### Example
Creating custom and managed rules with scope restriction and events:

[example of setting up rules](test/integ.rule.lit.ts)
3 changes: 3 additions & 0 deletions packages/@aws-cdk/aws-config/lib/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,5 @@
export * from './rule';
export * from './managed-rules';

// AWS::Config CloudFormation Resources:
export * from './config.generated';
130 changes: 130 additions & 0 deletions packages/@aws-cdk/aws-config/lib/managed-rules.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
import iam = require('@aws-cdk/aws-iam');
import sns = require('@aws-cdk/aws-sns');
import { Construct, Token } from '@aws-cdk/cdk';
import { ManagedRule, RuleProps } from './rule';

/**
* Construction properties for a AccessKeysRotated
*/
export interface AccessKeysRotatedProps extends RuleProps {
/**
* The maximum number of days within which the access keys must be rotated.
*
* @default 90 days
*/
readonly maxDays?: number;
}

/**
* Checks whether the active access keys are rotated within the number of days
* specified in `maxDays`.
*
* @see https://docs.aws.amazon.com/config/latest/developerguide/access-keys-rotated.html
*
* @resource AWS::Config::ConfigRule
*/
export class AccessKeysRotated extends ManagedRule {
constructor(scope: Construct, id: string, props: AccessKeysRotatedProps = {}) {
super(scope, id, {
...props,
identifier: 'ACCESS_KEYS_ROTATED',
inputParameters: {
...props.maxDays
? {
maxAccessKeyAge: props.maxDays
}
: {}
}
});
}
}

/**
* Construction properties for a CloudFormationStackDriftDetectionCheck
*/
export interface CloudFormationStackDriftDetectionCheckProps extends RuleProps {
/**
* Whether to check only the stack where this rule is deployed.
*
* @default false
*/
readonly ownStackOnly?: boolean;

/**
* The IAM role to use for this rule. It must have permissions to detect drift
* for AWS CloudFormation stacks. Ensure to attach `config.amazonaws.com` trusted
* permissions and `ReadOnlyAccess` policy permissions. For specific policy permissions,
* refer to https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/using-cfn-stack-drift.html.
*
* @default a role will be created
*/
readonly role?: iam.IRole;
}

/**
* Checks whether your CloudFormation stacks' actual configuration differs, or
* has drifted, from its expected configuration.
*
* @see https://docs.aws.amazon.com/config/latest/developerguide/cloudformation-stack-drift-detection-check.html
*
* @resource AWS::Config::ConfigRule
*/
export class CloudFormationStackDriftDetectionCheck extends ManagedRule {
private readonly role: iam.IRole;

constructor(scope: Construct, id: string, props: CloudFormationStackDriftDetectionCheckProps = {}) {
super(scope, id, {
...props,
identifier: 'CLOUDFORMATION_STACK_DRIFT_DETECTION_CHECK',
inputParameters: {
cloudformationRoleArn: new Token(() => this.role.roleArn)
}
});

this.scopeToResource('AWS::CloudFormation::Stack', props.ownStackOnly ? this.node.stack.stackId : undefined);

this.role = props.role || new iam.Role(this, 'Role', {
assumedBy: new iam.ServicePrincipal('config.amazonaws.com'),
managedPolicyArns: [
new iam.AwsManagedPolicy('ReadOnlyAccess', this).policyArn,
]
});
}
}

/**
* Construction properties for a CloudFormationStackNotificationCheck.
*/
export interface CloudFormationStackNotificationCheckProps extends RuleProps {
/**
* A list of allowed topics. At most 5 topics.
*/
readonly topics?: sns.ITopic[];
}

/**
* Checks whether your CloudFormation stacks are sending event notifications to
* a SNS topic. Optionally checks whether specified SNS topics are used.
*
* @see https://docs.aws.amazon.com/config/latest/developerguide/cloudformation-stack-notification-check.html
*
* @resource AWS::Config::ConfigRule
*/
export class CloudFormationStackNotificationCheck extends ManagedRule {
constructor(scope: Construct, id: string, props: CloudFormationStackNotificationCheckProps = {}) {
if (props.topics && props.topics.length > 5) {
throw new Error('At most 5 topics can be specified.');
}

super(scope, id, {
...props,
identifier: 'CLOUDFORMATION_STACK_NOTIFICATION_CHECK',
inputParameters: props.topics && props.topics.reduce(
(params, topic, idx) => ({ ...params, [`snsTopic${idx + 1}`]: topic.topicArn }),
{}
)
});

this.scopeToResource('AWS::CloudFormation::Stack');
}
}
Loading