Skip to content

Commit

Permalink
#21441 add Custom Policy Rule Constructs
Browse files Browse the repository at this point in the history
  • Loading branch information
watany-dev committed Sep 10, 2022
1 parent 62cbcde commit 50611ad
Show file tree
Hide file tree
Showing 11 changed files with 480 additions and 7 deletions.
56 changes: 54 additions & 2 deletions packages/@aws-cdk/aws-config/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -116,8 +116,60 @@ new config.CloudFormationStackNotificationCheck(this, 'NotificationCheck', {
### Custom rules

You can develop custom rules and add them to AWS Config. You associate each custom rule with an
AWS Lambda function, which contains the logic that evaluates whether your AWS resources comply
with the rule.
AWS Lambda function and Guard.

#### Custom Lambda Rules

Lambda function which contains the logic that evaluates whether your AWS resources comply with the rule.

```ts
// Lambda function containing logic that evaluates compliance with the rule.
const evalComplianceFn = new lambda.Function(this, "CustomFunction", {
code: lambda.AssetCode.fromInline(
"exports.handler = (event) => console.log(event);"
),
handler: "index.handler",
runtime: lambda.Runtime.NODEJS_14_X,
});

// A custom rule that runs on configuration changes of EC2 instances
const customRule = new config.CustomRule(this, "Custom", {
configurationChanges: true,
lambdaFunction: evalComplianceFn,
ruleScope: config.RuleScope.fromResource(config.ResourceType.EC2_INSTANCE),
});
```

#### Custom Policy Rules

Guard which contains the logic that evaluates whether your AWS resources comply with the rule.

```ts
const samplePolicyText = `
# This rule checks if point in time recovery (PITR) is enabled on active Amazon DynamoDB tables
let status = ['ACTIVE']
rule tableisactive when
resourceType == "AWS::DynamoDB::Table" {
configuration.tableStatus == %status
}
rule checkcompliance when
resourceType == "AWS::DynamoDB::Table"
tableisactive {
let pitr = supplementaryConfiguration.ContinuousBackupsDescription.pointInTimeRecoveryDescription.pointInTimeRecoveryStatus
%pitr == "ENABLED"
}
`;

new config.CustomPolicy(stack, "Custom", {
policyText: samplePolicyText,
enableDebugLog: true,
ruleScope: config.RuleScope.fromResources([
config.ResourceType.DYNAMODB_TABLE,
]),
});
```

### Triggers

Expand Down
137 changes: 132 additions & 5 deletions packages/@aws-cdk/aws-config/lib/rule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,52 @@ export class ManagedRule extends RuleNew {
}
}

/**
* The type of notification that triggers AWS Config to run an evaluation for a rule.
*/
enum MessageType {

/**
* Triggers an evaluation when AWS Config delivers a configuration item as a result of a resource change.
*/
CONFIGURATION_ITEM_CHANGE_NOTIFICATION = 'ConfigurationItemChangeNotification',

/**
* Triggers an evaluation when AWS Config delivers an oversized configuration item.
*/
OVERSIZED_CONFIGURATION_ITEM_CHANGE_NOTIFICATION = 'OversizedConfigurationItemChangeNotification',

/**
* Triggers a periodic evaluation at the frequency specified for MaximumExecutionFrequency.
*/
SCHEDULED_NOTIFICATION = 'ScheduledNotification',

/**
* Triggers a periodic evaluation when AWS Config delivers a configuration snapshot.
*/
CONFIGURATION_SNAPSHOT_DELIVERY_COMPLETED = 'ConfigurationSnapshotDeliveryCompleted',
}

/**
* Construction properties for a CustomRule.
*/
interface SourceDetail {
/**
* The source of the event, such as an AWS service,
* that triggers AWS Config to evaluate your AWS resources.
*
*/
readonly eventSource : string;
/**
* The frequency at which you want AWS Config to run evaluations for a custom rule with a periodic trigger.
*/
readonly maximumExecutionFrequency? : MaximumExecutionFrequency;
/**
* The type of notification that triggers AWS Config to run an evaluation for a rule.
*/
readonly messageType : MessageType;
}

/**
* Construction properties for a CustomRule.
*/
Expand Down Expand Up @@ -331,25 +377,24 @@ export class CustomRule extends RuleNew {
throw new Error('At least one of `configurationChanges` or `periodic` must be set to true.');
}

const sourceDetails: any[] = [];
const sourceDetails: SourceDetail[] = [];
this.ruleScope = props.ruleScope;

if (props.configurationChanges) {
sourceDetails.push({
eventSource: 'aws.config',
messageType: 'ConfigurationItemChangeNotification',
messageType: MessageType.CONFIGURATION_ITEM_CHANGE_NOTIFICATION,
});
sourceDetails.push({
eventSource: 'aws.config',
messageType: 'OversizedConfigurationItemChangeNotification',
messageType: MessageType.OVERSIZED_CONFIGURATION_ITEM_CHANGE_NOTIFICATION,
});
}

if (props.periodic) {
sourceDetails.push({
eventSource: 'aws.config',
maximumExecutionFrequency: props.maximumExecutionFrequency,
messageType: 'ScheduledNotification',
messageType: MessageType.SCHEDULED_NOTIFICATION,
});
}

Expand Down Expand Up @@ -391,6 +436,88 @@ export class CustomRule extends RuleNew {
}
}

/**
* Construction properties for a CustomPolicy.
*/
export interface CustomPolicyProps extends RuleProps {
/**
* The policy definition containing the logic for your AWS Config Custom Policy rule.
*/
readonly policyText: string;

/**
* The boolean expression for enabling debug logging for your AWS Config Custom Policy rule.
*
* @default false
*/
readonly enableDebugLog?: boolean;
}

/**
* A new custom policy.
*
* @resource AWS::Config::ConfigRule
*/
export class CustomPolicy extends RuleNew {
/** @attribute */
public readonly configRuleName: string;

/** @attribute */
public readonly configRuleArn: string;

/** @attribute */
public readonly configRuleId: string;

/** @attribute */
public readonly configRuleComplianceType: string;

constructor(scope: Construct, id: string, props: CustomPolicyProps) {
super(scope, id, {
physicalName: props.configRuleName,
});

if (!props.policyText || [...props.policyText].length === 0) {
throw new Error('Policy Text cannot be empty.');
}
if ( [...props.policyText].length > 10000 ) {
throw new Error('Policy Text is limited to 10,000 characters or less.');
}

const sourceDetails: SourceDetail[] = [];
this.ruleScope = props.ruleScope;

sourceDetails.push({
eventSource: 'aws.config',
messageType: MessageType.CONFIGURATION_ITEM_CHANGE_NOTIFICATION,
});
sourceDetails.push({
eventSource: 'aws.config',
messageType: MessageType.OVERSIZED_CONFIGURATION_ITEM_CHANGE_NOTIFICATION,
});
const rule = new CfnConfigRule(this, 'Resource', {
configRuleName: this.physicalName,
description: props.description,
inputParameters: props.inputParameters,
scope: Lazy.any({ produce: () => renderScope(this.ruleScope) }), // scope can use values such as stack id (see CloudFormationStackDriftDetectionCheck)
source: {
owner: 'CUSTOM_POLICY',
sourceDetails,
customPolicyDetails: {
enableDebugLogDelivery: props.enableDebugLog,
policyRuntime: 'guard-2.x.x',
policyText: props.policyText,
},
},
});

this.configRuleName = rule.ref;
this.configRuleArn = rule.attrArn;
this.configRuleId = rule.attrConfigRuleId;
this.configRuleComplianceType = rule.attrComplianceType;
this.isCustomWithChanges = true;
}
}

/**
* Managed rules that are supported by AWS Config.
* @see https://docs.aws.amazon.com/config/latest/developerguide/managed-rules-by-aws-config.html
Expand Down
1 change: 1 addition & 0 deletions packages/@aws-cdk/aws-config/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@
"@aws-cdk/aws-events-targets": "0.0.0",
"@aws-cdk/cdk-build-tools": "0.0.0",
"@aws-cdk/integ-runner": "0.0.0",
"@aws-cdk/integ-tests": "0.0.0",
"@aws-cdk/cfn2ts": "0.0.0",
"@aws-cdk/pkglint": "0.0.0",
"@types/jest": "^27.5.2",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
{
"Resources": {
"Custom8166710A": {
"Type": "AWS::Config::ConfigRule",
"Properties": {
"Source": {
"CustomPolicyDetails": {
"EnableDebugLogDelivery": true,
"PolicyRuntime": "guard-2.x.x",
"PolicyText": "\n# This rule checks if point in time recovery (PITR) is enabled on active Amazon DynamoDB tables\nlet status = ['ACTIVE']\n\nrule tableisactive when\n resourceType == \"AWS::DynamoDB::Table\" {\n configuration.tableStatus == %status\n}\n\nrule checkcompliance when\n resourceType == \"AWS::DynamoDB::Table\"\n tableisactive {\n let pitr = supplementaryConfiguration.ContinuousBackupsDescription.pointInTimeRecoveryDescription.pointInTimeRecoveryStatus\n %pitr == \"ENABLED\"\n}\n"
},
"Owner": "CUSTOM_POLICY",
"SourceDetails": [
{
"EventSource": "aws.config",
"MessageType": "ConfigurationItemChangeNotification"
},
{
"EventSource": "aws.config",
"MessageType": "OversizedConfigurationItemChangeNotification"
}
]
},
"Scope": {
"ComplianceResourceTypes": [
"AWS::DynamoDB::Table"
]
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"version":"20.0.0"}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"version": "20.0.0",
"testCases": {
"aws-cdk-config-custompolicy-integ/DefaultTest": {
"stacks": [
"aws-cdk-config-custompolicy"
],
"assertionStack": "aws-cdk-config-custompolicy-integ/DefaultTest/DeployAssert"
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
{
"version": "20.0.0",
"artifacts": {
"Tree": {
"type": "cdk:tree",
"properties": {
"file": "tree.json"
}
},
"aws-cdk-config-custompolicy": {
"type": "aws:cloudformation:stack",
"environment": "aws://unknown-account/unknown-region",
"properties": {
"templateFile": "aws-cdk-config-custompolicy.template.json",
"validateOnSynth": false
},
"metadata": {
"/aws-cdk-config-custompolicy/Custom/Resource": [
{
"type": "aws:cdk:logicalId",
"data": "Custom8166710A"
}
]
},
"displayName": "aws-cdk-config-custompolicy"
},
"awscdkconfigcustompolicyintegDefaultTestDeployAssert4EE21D3A": {
"type": "aws:cloudformation:stack",
"environment": "aws://unknown-account/unknown-region",
"properties": {
"templateFile": "awscdkconfigcustompolicyintegDefaultTestDeployAssert4EE21D3A.template.json",
"validateOnSynth": false
},
"displayName": "aws-cdk-config-custompolicy-integ/DefaultTest/DeployAssert"
}
}
}
Loading

0 comments on commit 50611ad

Please sign in to comment.